/* Copyright 2011 Rok Lenarčič 
 * 
 * This file is a part of MicroFn.
 * 
 * MicroFn is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * MicroFn is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> */

package net.rlenar.microfn.func;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Map.Entry;

import net.rlenar.microfn.CollectionFactory;
import net.rlenar.microfn.EmptyIterable;
import net.rlenar.microfn.Fn;
import net.rlenar.microfn.IFn;
import net.rlenar.microfn.SingleCheckIterator;
import net.rlenar.microfn.desc.FnName;


/**
 * This class contains Fns and their corresponding method representations which
 * deal mainly with creation and manipulation of Fns.
 * 
 * @author Rok Lenarcic
 *
 */
@SuppressWarnings("unchecked")
public class Func {
    ////////////////////////////////////////////////////////////////////////////////
    // ARITHMETIC
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Sums two integers
     */
    public static final Fn plus = new Plus(); 
    private static class Plus extends Fn {
        private Plus() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() + ((Number)args[1]).longValue();
        }
    }  
    /**
     * Multiplies two integers
     */
    public static final Fn mul = new Multiply(); 
    private static class Multiply extends Fn {
        private Multiply() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() * ((Number)args[1]).longValue();
        }
    } 
    /**
     * Minus two integers
     */
    public static final Fn minus = new Minus(); 
    private static class Minus extends Fn {
        private Minus() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() - ((Number)args[1]).longValue();
        }
    } 
    /**
     * Sums two floating point numbers
     */
    public static final Fn plusF = new PlusF(); 
    private static class PlusF extends Fn {
        private PlusF() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).doubleValue() + ((Number)args[1]).doubleValue();
        }
    } 
    /**
     * Multiplies two floating point numbers
     */
    public static final Fn mulF = new MultiplyF(); 
    private static class MultiplyF extends Fn {
        private MultiplyF() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).doubleValue() * ((Number)args[1]).doubleValue();
        }
    } 
    /**
     * Minus two floating point numbers
     */
    public static final Fn minusF = new MinusF(); 
    private static class MinusF extends Fn {
        private MinusF() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).doubleValue() + ((Number)args[1]).doubleValue();
        }
    } 
    /**
     * Sums two integers
     */
    public static final Fn inc = new Increase(); 
    private static class Increase extends Fn {
        private Increase() {
            super(1,1);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() + 1;
        }
    } 
    
    ////////////////////////////////////////////////////////////////////////////////
    // ASSOC
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Associates key with the value on the collection or map.
     * 
     * Parameters are collection, key, value.
     * Behaviour depends on the type of collection parameter.
     * 
     * Maps: Associates key with the value, returning the value that is being removed (or null).
     * Sets: Ignores value parameter, returns null if key wasn't in set already otherwise returns key.
     * List: Key is numerical index into the list, swaps the item at the index, 
     * returns the value that is being swapped out, otherwise null. If index 
     * points just beyond the last element, then element is added and null returned
     * 
     */ 
    public static final Fn assoc = new Assoc();
    private static class Assoc extends Fn {
        Assoc() {
            super(3,3);
        }
        @Override
        public Object doFunc(Object... args) {
            if (args[0] instanceof List<?>) {
                int index = ((Number)args[1]).intValue();
                List<Object> coll = ((List<Object>)args[0]);
                if (index >= 0 || index < coll.size()) {
                    Object ret = coll.get(index);
                    coll.set(index, args[2]);
                    return ret;
                } else if (index == coll.size()) {
                    coll.add(args[2]);
                    return null;
                } else {
                    return null;
                }
            } else if (args[0] instanceof Map) {
                return ((Map<Object,Object>)args[0]).put(args[1], args[2]);
            } else {
                return ((Set<Object>)args[0]).add(args[1]) ?  null : args[1];
            }
        }
        
    }
    /**
     * Associates key with the value on the collection or map.
     * 
     * Parameters are collection, key, value.
     * Behaviour depends on the type of collection parameter.
     * 
     * Maps: Associates key with the value, returning the value that is being removed (or null).
     * Sets: Ignores value parameter, returns null if key wasn't in set already otherwise returns key.
     * List: Key is numerical index into the list, swaps the item at the index, 
     * returns the value that is being swapped out, otherwise null. If index 
     * points just beyond the last element, then element is added and null returned
     * 
     * @param collection 
     * @param keyOrIndex key for maps and sets or numerical index for lists
     * @param value
     * @return 
     */
    public static <T> T assoc(Object collection, Object keyOrIndex, Object value) {
        return (T)assoc.invoke(collection, keyOrIndex, value); 
    }

    ////////////////////////////////////////////////////////////////////////////////
    // CONJ
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Parameters are one collection or map followed by at least one value.
     * Adds value to a collection. Supports Lists, Sets and Maps
     * 
     * Maps: Each value must a 2-element Iterable
     * 
     * Returns true if resulting collection has changed 
     */ 
    public static final Fn conj = new Conj();
    private static class Conj extends Fn {
        Conj() {
            super(2);
        }
        @Override
        public Boolean doFunc(Object... args) {
            if (args[0] instanceof List<?>) {
                List<Object> coll = ((List<Object>)args[0]);
                for (int i = 1;i < args.length;i++) {
                    coll.add(args[i]);
                }
                return true;
            } else if (args[0] instanceof Map) {
                Map<Object,Object> m = ((Map<Object,Object>)args[0]);
                for (int i = 1;i < args.length;i++) {
                    Iterator<Object> iter = ((Iterable<Object>)args[i]).iterator();
                    m.put(iter.next(), iter.next());
                }
                return true;
            } else {
                Boolean res = false;
                Set<Object> s = ((Set<Object>)args[0]);
                for (int i = 1;i < args.length;i++) {
                    res = s.add(args[i]) || res;
                }
                return res;
            }
        }
        
    }
    /**
     * Adds value to a collection. Supports Lists, Sets and Maps
     * 
     * Maps: Each value is a 2-element Iterable
     * 
     * @param collectionAndValues one collection followed by at least one value
     * @return true if resulting collection has changed 
     */
    public static Boolean conj(Object collectionAndValues) {
        return (Boolean)conj.invoke(collectionAndValues);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // CONSTANT
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Function returns a new fn which returns constant value regardless of parameters.
     * Parameter is the desired constant value.
     * 
     */
    public static final Fn constant = new Constant();
    private static class Constant extends Fn {
        private Constant() {
            super(1,1);
        }
        @Override
        public Fn doFunc(Object... args) {
            final Object constant = args[0];
            return new Fn() {
                @Override
                public Object doFunc(Object... args) {
                    return constant;
                }
            };
        }
        
    }
    
    /**
     * Function returns a new fn which returns constant value regardless of parameters.
     * Parameter is the desired constant value.
     *
     * @param c desired constant 
     * @return Fn that returns parameter c, regardless of arguments
     */
    public static Fn constant(Object c) {
        return (Fn)constant.invoke(c);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // CONTAINS
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns true if the parameter iterable or map contains the object
     * First parameter is a map or an iterable
     * Second parameter is an object to search for
     */
    public static final Fn contains = new Contains();
    private static class Contains extends Fn {
        private Contains() {
            super(2,2);
        }
        @Override
        public Boolean doFunc(Object... args) {
            if (args[0] instanceof Map) {
                return ((Map<Object,Object>)args[0]).containsKey(args[1]);
            } else if (args[0] instanceof Collection) {
                return ((Collection<Object>)args[0]).contains(args[1]);
            } else {
                for (Object elem : (Iterable<Object>)args[0]) {
                    if (elem == null) {
                        return args[1] == null;
                    }
                    if (elem.equals(args[1])) {
                        return true;
                    }
                }
                return false;
            }
        }
        
    }
    /**
     * Returns true if the iterable or map contains the object
     * @param iter a map or an iterable
     * @param obj an object to search for
     * @return true if iterable or map contains the object
     */
    public static Boolean contains(Object iter, Object obj) {
        return (Boolean)contains.invoke(iter, obj);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // COUNT
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Counts the elements. Note that bare iterables take O(n) time. 
     * Works on all objects. Iterables, maps, strings and arrays return the number of elements they
     * contain. Other objects return 0.
     * 
     * Takes one parameter, an object.
     */ 
    public static final Fn count = new Count();
    private static class Count extends Fn {
        private Count() {
            super(1,1);
        }
        @Override
        public Integer doFunc(Object... args) {
            if (args[0] == null) {
                return 0;
            } else if (args[0] instanceof Collection<?>) {
                return ((Collection<?>)args[0]).size();
            } else if (args[0] instanceof Iterable<?>) {
                Iterator<?> i = ((Iterable<?>)args[0]).iterator();
                int count = 0;
                while (i.hasNext()) {
                    i.next();
                    count++;
                }
                return count;
            } else if (args[0] instanceof Map<?,?>) {
                return ((Map<?,?>)args[0]).size();
            } else if (args[0] instanceof CharSequence) {
                return ((CharSequence)args[0]).length();
            } else if (args[0].getClass().isArray()) {
                return Array.getLength(args[0]);
            } else {
                return 0;
            }
        }
        
    }
    /**
     * Counts the elements. Note that bare iterables take O(n) time. 
     * Works on all objects. Iterables, maps, strings and arrays return the number of elements they
     * contain. Other objects return 0.
     * 
     * @param obj an object  
     * @return 
     */
    public static Integer count(Object obj) {
        return (Integer)count.invoke(obj);
    }   

    ////////////////////////////////////////////////////////////////////////////////
    // DISSOC
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Dissociates (removes) key from the collection. Returns the value removed (can be null) or null otherwise. Works
     * on maps, sets, lists. In case of lists, the key is an index.
     * 
     * List: Key is numerical, removes the element,  
     * points just beyond the last element, then element is added and null returned
     *  
     * First parameter is a map or a set or a list
     * Second parameter is a key for maps and sets or numerical index for lists
     */ 
    public static final Fn dissoc = new Dissoc();
    private static class Dissoc extends Fn {
        private Dissoc() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            if (args[0] instanceof List<?>) {
                int index = ((Number)args[1]).intValue();
                List<Object> coll = ((List<Object>)args[0]);
                if (index >= 0 || index < coll.size()) {
                    return coll.remove(index);
                } else {
                    return null;
                }
            } else if (args[0] instanceof Map) {
                return ((Map<Object,Object>)args[0]).remove(args[1]);
            } else {
                return ((Set<Object>)args[0]).remove(args[1]) ? args[1] : null;
            }
        }
        
    }
    /**
     * Dissociates (removes) key from the collection. Returns the value removed (can be null) or null otherwise. Works
     * on maps, sets, lists. In case of lists, the key is an index.
     * 
     * List: Key is numerical, removes the element,  
     * points just beyond the last element, then element is added and null returned
     *  
     * @param collection a map or a set or a list
     * @param keyOrIndex a key for maps and sets or numerical index for lists
     * @return 
     */
    public static <T> T dissoc(Object collection, Object keyOrIndex) {
        return (T)dissoc.invoke(collection, keyOrIndex);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // FIRST
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Function returns first element of an iterable. If iterable is empty it returns null.
     * 
     */
    public static final Fn first = new First();
    private static class First extends Fn {
        private First() {
            super(1);
        }
        @Override
        public Object doFunc(Object... args) {
            Iterator<? extends Object> f = ((Iterable<? extends Object>)args[0]).iterator();
            if (f.hasNext()) {
                return f.next();
            }
            return null;
        }
        
    }
    /**
     * Function returns first element of an iterable. If iterable is empty it returns null.
     * 
     * @param iter an iterable
     * @return first element of an iterable or null
     */
    public static <T> T first(Object iter) {
        return (T)first.invoke(iter);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // EQUALS
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Function returns true if all arguments are equal. Null values are equal only to other null values.
     * Takes one or more objects.
     * 
     */ 
    public static final Fn eq = new Equals();
    private static class Equals extends Fn {
        private Equals() {
            super(1);
        }
        @Override
        public Boolean doFunc(Object... args) {
            Object firstObj = args[0];
            for (int i = 1; i < args.length;i++) {
                if (firstObj == null) {
                    if (args[i] != null) {
                        return false;
                    }
                } else {
                    if (args[i] == null) {
                        return false;
                    } else if (!firstObj.equals(args[i])) {
                        return false;
                    }
                }
            }
            return true;
        }
        
    }
    /**
     * Function returns true if all arguments are equal. Null values are equal only to other null values.
     * Requires at least 1 parameter.
     * 
     * @param objs objects to compare for equality
     * @return true if all parameters are equal
     */
    public static Boolean eq(Object... objs) {
        return (Boolean)eq.invoke((Object[])objs);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // GET
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Get an element of a map, set or list. 
     * Use Nth instead for iterables. 
     * Returns default if collection parameter is not a map, a set or a list.
     * Provided default is used when the key doesn't exist.
     *  
     * In case of list, the default is also used when the list element with that index is
     * null;
     * 
     * First parameter the collection.
     * Second parameter is the key or index
     * Third optional parameter is a default value (default value is null if parameter is omitted)
     */ 
    public static final Fn get = new Get();
    private static class Get extends Fn {
        private Get() {
            super(2,3);
        }
        @Override
        public Object doFunc(Object... args) {
            Object defaultRet = null;
            if (args.length == 3) {
                defaultRet = args[2];
            }
            if (args[0] instanceof Map){
                Object key = args[1];
                Map<?,?> coll = (Map<?,?>)args[0];
                if (coll.containsKey(key)) {
                    return coll.get(key);
                } else {
                    return defaultRet;
                }
            } else if (args[0] instanceof List) {
                int index = ((Number)args[1]).intValue();
                List<?> coll = ((List<?>)args[0]);
                if (index < 0 || index >= coll.size()) {
                    return defaultRet;
                }
                Object ret = coll.get(index);
                if (ret != null) {
                    return ret;
                } else {
                    return defaultRet;
                }
            } else if (args[0] instanceof Set) {
                Object key = args[1];
                Set<?> coll = (Set<?>)args[0];
                if (coll.contains(key)) {
                    return key;
                } else {
                    return defaultRet;
                }
            } else {
                return defaultRet;
            }
        }
        
    }
    /**
     * Get an element of a map, set or list. 
     * Use Nth instead for iterables. 
     * Returns null if collection parameter is not a map, a set or a list.
     * Returns null when the key doesn't exist.
     * 
     * @param collection a map, set or list. 
     * @param keyOrIndex key for maps and sets or numerical index for lists
     * @return 
     */
    public static <T> T get(Object collection, Object keyOrIndex) {
        return (T)get.invoke(collection, keyOrIndex);
    }
    /**
     * Get an element of a map, set or list. 
     * Use Nth instead for iterables. 
     * Returns default if collection parameter is not a map, a set or a list.
     * Provided default is used when the key doesn't exist.
     *  
     * In case of list, the default is also used when the list element with that index is
     * null;
     * 
     * @param collection a map, set or list. 
     * @param keyOrIndex key for maps and sets or numerical index for lists
     * @param defaultRet default return 
     * @return 
     */
    public static <T> T get(Object collection, Object keyOrIndex, Object defaultRet) {
        return (T)get.invoke(collection, keyOrIndex, defaultRet);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // IDENTITY
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * This single parameter function returns it's argument.
     * 
     */
    public static final Fn identity = new Identity();
    private static class Identity extends Fn {
        private Identity() {
            super(1,1);
        }
        @Override
        public Object doFunc(Object... args) {
            return args[0];
        }
        
    }
    /**
     * This single parameter function returns it's argument.
     * 
     * @param obj 
     * @return the parameter
     */
    public static <T> T identity(Object obj) {
        return (T)identity.invoke(obj);
    }
    //// TODO : JAVADOCS
    ////////////////////////////////////////////////////////////////////////////////
    // MAPGET
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Get an element of a map, set or list. 
     * Use Nth instead for iterables. 
     * Returns default if collection parameter is not a map, a set or a list.
     * Provided default is used when the key doesn't exist.
     *  
     * In case of list, the default is also used when the list element with that index is
     * null;
     * 
     * First parameter the collection.
     * Second parameter is the key or index
     * Third optional parameter is a default value (default value is null if parameter is omitted)
     */ 
    public static final Fn mapGet = new MapGet();
    private static class MapGet extends Fn {
        private MapGet() {
            super(2,3);
        }
        @Override
        public Object doFunc(Object... args) {
            if (args.length == 3) {
                Map<?,?> coll = (Map<?,?>)args[0];
                if (coll.containsKey(args[1])) {
                    return coll.get(args[1]);
                } else {
                    return args[2];
                }
            } else {
                return ((Map<?,?>)args[0]).get(args[1]);
            }
        }
        
    }
    /**
     * Get an element of a map, set or list. 
     * Use Nth instead for iterables. 
     * Returns null if collection parameter is not a map, a set or a list.
     * Returns null when the key doesn't exist.
     * 
     * @param collection a map, set or list. 
     * @param keyOrIndex key for maps and sets or numerical index for lists
     * @return 
     */
    public static <T> T mapGet(Object collection, Object key) {
        return (T)mapGet.invoke(collection, key);
    }
    /**
     * Get an element of a map, set or list. 
     * Use Nth instead for iterables. 
     * Returns default if collection parameter is not a map, a set or a list.
     * Provided default is used when the key doesn't exist.
     *  
     * In case of list, the default is also used when the list element with that index is
     * null;
     * 
     * @param collection a map, set or list. 
     * @param keyOrIndex key for maps and sets or numerical index for lists
     * @param defaultRet default return 
     * @return 
     */
    public static <T> T mapGet(Object collection, Object key, Object defaultRet) {
        return (T)mapGet.invoke(collection, key, defaultRet);
    }   
    
    ////////////////////////////////////////////////////////////////////////////////
    // MERGEPUT
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn mergePut = new MergePut();
    private static class MergePut extends Fn {  
        private MergePut() {
            super(4,4);
        }
        @Override
        public Object doFunc(Object... args) {
            Map<Object, Object> map = (Map<Object,Object>)args[0];
            IFn fn = (IFn)args[1];
            Object ret = map.get(args[2]);
            if (ret == null) {
                return map.put(args[2], args[3]);
            } else {
                return map.put(args[2], fn.invoke(ret, args[3]));
            }
        }
        
    }
    /**
     * It puts the key-value pair into the map, but if key is already present and value it points to is not null
     * then value is replaced by the result of the function acting on the old value bound to the key and the new
     * value given. If the map didn't contain the key or the key mapped to null, then it just does the normal "put".
     * Returns the value swapped out or null.
     * 
     * @param map A map of some kind
     * @param f A function that takes 2 arguments. First one is the old value for the key, second one is the new value 
     * @return Value that has been replaced, null otherwise
     */
    public static <T> T mergePut(Object map, Object f, Object key, Object value) {
        return (T)mergePut.invoke(map, f, key, value);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // NOTEQUALS
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn notEq = new NotEquals();
    private static class NotEquals extends Fn {
        private NotEquals() {
            super(1);
        }
        @Override
        public Boolean doFunc(Object... args) {
            Object firstObj = args[0];
            if (args.length == 1) {
                return false; // single element is equal to itself
            }
            for (int i = 1;i < args.length;i++) {
                if (firstObj == null) {
                    if (args[i] == null) {
                        return false;
                    }
                } else {
                    if (args[i] != null && firstObj.equals(args[i])) {
                        return false;
                    } 
                }
            }
            return true;
        }
        
    }
    /**
     * Function returns true if all arguments are equal. Null values are equal only to other null values.
     * @param objs object to compare for equality
     * @return true if all parameters are equal
     */
    public static Boolean notEq(Object... objs) {
        return (Boolean)eq.invoke((Object[])objs);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // NTH
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn nth = new Nth();
    private static class Nth extends Fn {
        private Nth() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            if (args[0] instanceof List<?>) {
                return ((List<?>)args[0]).get(((Number)args[1]).intValue());
            } else if (args[0] instanceof CharSequence){
                return ((CharSequence)args[0]).charAt(((Number)args[1]).intValue());
            } else {
                int i = 0;
                long desired = ((Number)args[1]).longValue();
                for (Object elem : (Iterable<Object>)args[0]) {
                    if (i == desired) {
                        return elem;
                    }
                    i++;
                }
                throw new RuntimeException("Nth index out of bounds: " + i);
            }
        }
        
    };
    /**
     * N-th element of an iterable
     * @param iter
     * @param index 
     * @return 
     */
    public static <T> T nth(Object iter, Object index) {
        return (T)nth.invoke(iter, index);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // PIPE
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Takes the first parameter and pipes it though the rest of parameters which are Fns
     */
    public static final Fn pipe = new Pipe();
    private static class Pipe extends Fn {
        private Pipe() {
            super(1);
        }
        @Override
        public Object doFunc(Object... args) {
            Object result = args[0];
            for (int i = 1;i < args.length;i++) {
                result = ((IFn)args[i]).invoke(result);
            }
            return result;
            
        }
    }
    public static <T> T pipe(Object... objectAndFns) {
        return (T)pipe.invoke(objectAndFns);
    }       
    
    ////////////////////////////////////////////////////////////////////////////////
    // PREDICATES
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Works on Strings and Iterables
     */
    public static final Fn empty = new Empty();
    private static class Empty extends Fn {
        private Empty() {
            super(1,1);
        }
        @Override
        protected Boolean doFunc(Object... args) {
            if (args[0] instanceof Iterable) {
                return ((Iterable<?>)args[0]).iterator().hasNext();
            }
            if (args[0] instanceof CharSequence) {
                return ((CharSequence)args[0]).length() == 0;               
            }
            return ((Map<?,?>)args[0]).size() == 0;
        }
    }
    public static Boolean empty(Object stringOrIterOrMap) {
        return (Boolean)empty.invoke(stringOrIterOrMap);
    }
    /**
     * Works on Strings and Iterables
     */
    public static final Fn nullOrEmpty = new NullOrEmpty();
    private static class NullOrEmpty extends Fn {
        private NullOrEmpty() {
            super(1,1);
        }
        @Override
        protected Boolean doFunc(Object... args) {
            if (args[0] == null) {
                return true;
            }
            if (args[0] instanceof Iterable) {
                return ((Iterable<?>)args[0]).iterator().hasNext();
            }
            if (args[0] instanceof CharSequence) {
                return ((CharSequence)args[0]).length() == 0;       
            }
            return ((Map<?,?>)args[0]).size() == 0;
        }
    }   
    public static Boolean nullOrEmpty(Object stringOrIterOrMap) {
        return (Boolean)nullOrEmpty.invoke(stringOrIterOrMap);
    }   
    /**
     * Works on Strings and Iterables
     */ 
    public static final Fn notNullOrEmpty = new NotNullOrEmpty();
    private static class NotNullOrEmpty extends Fn {
        private NotNullOrEmpty() {
            super(1,1);
        }
        @Override
        protected Boolean doFunc(Object... args) {
            if (args[0] == null) {
                return false;
            }
            if (args[0] instanceof Iterable) {
                return !((Iterable<?>)args[0]).iterator().hasNext();
            }
            if (args[0] instanceof CharSequence) {
                return ((CharSequence)args[0]).length() != 0;   
            }
            return ((Map<?,?>)args[0]).size() != 0;
        }
    }
    public static Boolean notNullOrEmpty(Object stringOrIterOrMap) {
        return (Boolean)notNullOrEmpty.invoke(stringOrIterOrMap);
    }   
    public static final Fn and = new And();
    private static class And extends Fn {
        private And() {
            super(1);
        }
        @Override
        protected Object doFunc(Object... args) {
            for (Object arg : args) {
                if (isNil(arg)) {
                    return arg;
                }
            }
            return args[args.length - 1];
        }
    }   
    public static final Fn or = new Or();
    private static class Or extends Fn {
        private Or() {
            super(1);
        }
        @Override
        protected Object doFunc(Object... args) {
            for (Object arg : args) {
                if (!isNil(arg)) {
                    return arg;
                }
            }
            return args[args.length - 1];
        }
    }   
    public static final Fn lt = new LessThan();
    private static class LessThan extends Fn {
        private LessThan() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() < ((Number)args[1]).longValue();
        }
    }   
    public static final Fn le = new LessOrEqual();
    private static class LessOrEqual extends Fn {
        private LessOrEqual() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() <= ((Number)args[1]).longValue();
        }
    }   
    public static final Fn gt = new GreaterThan();
    private static class GreaterThan extends Fn {
        private GreaterThan() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() > ((Number)args[1]).longValue();
        }
    }   
    public static final Fn ge = new GreaterOrEqual(); 
    private static class GreaterOrEqual extends Fn {
        private GreaterOrEqual() {
            super(2,2);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() >= ((Number)args[1]).longValue();
        }
    }
    public static final Fn isZero = new Zero(); 
    private static class Zero extends Fn {
        private Zero() {
            super(1,1);
        }
        @Override
        protected Object doFunc(Object... args) {
            return ((Number)args[0]).longValue() == 0;
        }
    }  

    ////////////////////////////////////////////////////////////////////////////////
    // SECOND
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Function returns second element of an iterable. If iterable has less than two elements it returns null.
     * 
     */
    public static final Fn second = new Second();
    private static class Second extends Fn {
        private Second() {
            super(1);
        }
        @Override
        public Object doFunc(Object... args) {
            Iterator<? extends Object> f = ((Iterable<? extends Object>)args[0]).iterator();
            if (f.hasNext()) {
                f.next();
                if (f.hasNext()) {
                    return f.next();
                }
            }
            return null;
        }
        
    }
    /**
     * Function returns second element of an iterable. If iterable has less than two elements it returns null.
     * 
     * @param iter an iterable
     * @return first element of an iterable or null
     */
    public static <T> T second(Object iter) {
        return (T)second.invoke(iter);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // STR
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn str = new Str();
    private static class Str extends Fn {
        @Override
        public String doFunc(Object... args) {
            StringBuilder buf = new StringBuilder("");
            for (Object arg : args) {
                buf.append(arg);
            }
            return buf.toString();
        }
        
    }
    /**
     * Converts arguments into strings and concatenates them. 
     * @param args objects to be concatenated into a string
     * @return concatenated arguments as strings or empty string if no arguments 
     */
    public static String str(Object... args) {
        return (String)str.invoke(args);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // STRSEQ
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn strSeq = new StrSeq();
    private static class StrSeq extends Fn {
        private StrSeq() {
            super(1,1);
        }
        @Override
        public String doFunc(Object... args) {
            StringBuilder buf = new StringBuilder("");
            Iterable<Object> iter = (Iterable<Object>)args[0];
            for (Object arg : iter) {
                buf.append(arg);
            }
            return buf.toString();
        }
        
    }
    /**
     * Converts argument iterable of objects into strings and concatenates them. 
     * @param args iterable objects to be concatenated into a string
     * @return concatenated arguments as strings or empty string if no arguments 
     */
    public static String strSeq(Object singleIter) {
        return (String)strSeq.invoke(singleIter);
    }   
    
    ////////////////////////////////////////////////////////////////////////////////
    // TIME
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn time = new Time();
    private static class Time extends Fn {
        private Time() {
            super(1);
        }
        @Override
        public Object doFunc(Object... args) {
            IFn fun = (IFn)args[0];
            List<Object> argAccumulator = new ArrayList<Object>();
            for (int i = 1;i < args.length;i++) {
                argAccumulator.add(args[i]);
            }
            Object[] innerFnArgs = argAccumulator.toArray();
            long start = System.nanoTime();
            Object ret = fun.invoke(innerFnArgs);
            long total = System.nanoTime() - start;
            FnName fnName = args[0].getClass().getAnnotation(FnName.class);
            if (fnName == null) {
                System.out.println("Executed in  " + total / 1000000d + " msec.");
            } else {
                System.out.println("Executed " + fnName.value() + " in  " + total / 1000000d + " msec.");               
            }
            return ret;
            
        }
    }
    public static <T> T time(Object... fnAndArgs) {
        return (T)time.invoke(fnAndArgs);
    }
        
	////////////////////////////////////////////////////////////////////////////////
	// APPLY
	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Method applies sequence of objects as arguments to another Fn.
	 * First argument is the target function.
	 * Sequence of objects is last argument and can optionally be preceded by any number of objects which will
	 * precede the objects in the sequence in the argument list of the fn call.
	 */
	public static final Fn apply = new Apply();
	private static class Apply extends Fn {
		private Apply() {
			super(2);
		}
		@Override
		public Object doFunc(Object... args) {
			IFn fun = (IFn)args[0];
			List<Object> argAccumulator = new ArrayList<Object>();
			for (int i = 1;i < args.length - 1;i++) {
				argAccumulator.add(args[i]);
			}
			for (Object elem : (Iterable<?>)args[args.length - 1]) {
				argAccumulator.add(elem);
			}
			return fun.invoke(argAccumulator.toArray());
		}
		
	}
	/**
	 * Method applies sequence of objects as arguments to another Fn.
	 * First argument is the target function.
	 * Sequence of objects is last argument and can optionally be preceded by any number of objects which will
	 * precede the objects in the sequence in the argument list of the fn call.
	 * @param fnArgsAndSeq has to be at least one fn followed by one iterable. 
	 * Can have by any other objects between them, which will be used as params, preceding the objects from iterable.  
	 * @return result of the function call with the supplied arguments
	 */
	public static <T> T apply(Object... fnArgsAndSeq) {
		return (T)apply.invoke(fnArgsAndSeq);
	}

	////////////////////////////////////////////////////////////////////////////////
	// COMPOSE
	////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Returns a new function which is a composition of (one or more) argument functions. Functions are
	 * evaluated left to right, starting with leftmost function and using the result of the function call as an argument to the next Fn. 
	 * Note that all but leftmost function will get 1 argument.
	 */
	public static final Fn comp = new Compose();
	private static class Compose extends Fn {
		private Compose() {
			super(1);
		}
		@Override
		public Fn doFunc(Object... args) {
			final IFn[] functions = new IFn[args.length];
			for (int i = 0;i < args.length;i++) {
				functions[i] = (IFn)args[i];
			}
			class WorkerFn extends Fn {
				private IFn[] funs = functions;
				@Override
				public Object doFunc(Object... args) {
					Object ret = funs[0].invoke(args);
					for (int i = 1;i < funs.length;i++) {
						ret = funs[i].invoke(ret);
					}
					return ret;
				}
				
			};
			return new WorkerFn();
		}
		
	}
	/**
	 * Returns a new function which is a composition of (one or more) argument functions. Functions are
	 * evaluated left to right, starting with leftmost function and using the result of the function call as an argument to the next Fn. 
	 * Note that all but leftmost function will get 1 argument.
	 * @param functions one or more Fns 
	 * @return 
	 */
	public static Fn comp(Object... functions) {
		return (Fn)comp.invoke(functions);
	}	
	/**
	 * Returns a new function which is a composition of (one or more) argument functions. Functions are
	 * evaluated left to right, starting with leftmost function and using the result of the function call as an argument to the next Fn. 
	 * Note that all but leftmost function will get 1 argument.
	 * @param functions one or more Fns 
	 * @return 
	 */
	public static Fn $C(Object... functions) {
		return (Fn)comp.invoke(functions);
	}	

	////////////////////////////////////////////////////////////////////////////////
	// JUXT
	////////////////////////////////////////////////////////////////////////////////

	/**
	 * This function takes one or more functions as arguments and it returns a new function 
	 * which when invoked returns a list of objects, one for each function given at creation.
	 * Value of each object in the list is the result of the invocation of the corresponding function.
	 * 
	 * Example:
	 * 
	 * Given Fns plusTwo and minusThree and str:
	 * juxt(plusTwo, minusThree, str) -> anonymous Fn: anonFn
	 * anonFn(3) -> [5, 0, "3"]
	 * 
	 */
	public static final Fn juxt = new Juxtapose();
	private static class Juxtapose extends Fn {
		private Juxtapose() {
			super(1);
		}
		@Override
		public Fn doFunc(Object... args) {
			final IFn[] functions = new IFn[args.length];
			for (int i = 0;i < args.length;i++) {
				functions[i] = (IFn)args[i];
			}
			class WorkerFn extends Fn {
				private IFn[] funs = functions;
				@Override
				public List<? extends Object> doFunc(Object... args) {
					List<Object> retList = CollectionFactory.activeCollectionFactory.getListInstance(funs.length);	
					for (IFn fn : funs) {
						retList.add(fn.invoke(args));
					}
					return retList;
				}
			}
			return new WorkerFn();
		}
		
	}
	/**
	 * This function takes one or more functions as arguments and it returns a new function 
	 * which when invoked returns a list of objects, one for each function given at creation.
	 * Value of each object in the list is the result of the invocation of the corresponding function.
	 * 
	 * Example:
	 * 
	 * Given Fns plusTwo and minusThree and str:
	 * juxt(plusTwo, minusThree, str) -> anonymous Fn: anonFn
	 * anonFn(3) -> [5, 0, "3"]
	 * 
	 * @param functions one or more Fns 
	 * @return 
	 */
	public static Fn juxt(Object... functions) {
		return (Fn)juxt.invoke(functions);
	}	
    ////////////////////////////////////////////////////////////////////////////////
    // MEMBERFN
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns a new function which invokes member metod of a class.
     * Parameters are a class object and string representation of member name and class objects representing parameter types.
     * Returned function takes one more parameter than the member method, first parameter being the object
     * on which to invoke the method.
     * 
     */ 
	public static final Fn memberFn = new MemberFn();
	private static class MemberFn extends Fn {
        private MemberFn() {
            super(2);
        }
        @Override
        public Fn doFunc(Object... args) throws SecurityException, NoSuchMethodException {
            final Class<?> clazz = (Class<?>)args[0];
            final String member = (String)args[1];
            Class<?>[] params = new Class<?>[args.length - 2];
            if (params.length > 0) {
                System.arraycopy(args, 2, params, 0, params.length);
            }
            final Method method = clazz.getMethod(member, params);
            class WorkerFn extends Fn {
                private WorkerFn(int argumentNum) {
                    super(argumentNum + 1, argumentNum + 1);
                    params = new Object[argumentNum];
                }                
                private final Object[] params;
                @Override
                public Object doFunc(Object... args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
                    System.arraycopy(args, 1, params, 0, params.length);
                    return method.invoke(args[0], params);
                }          
            };
            return new WorkerFn(params.length);
        }	    
	}
    public static Fn memberFn(Object... classAndMemberNameAndParamClasses) {
        return (Fn)memberFn.invoke(classAndMemberNameAndParamClasses);
    }	
	////////////////////////////////////////////////////////////////////////////////
	// NON NULL COMPOSE
	////////////////////////////////////////////////////////////////////////////////
	/**
	 * Returns a new function which is a composition of (one or more) argument functions. Functions are
	 * evaluated left to right, starting with leftmost function and using the result of the function call as an argument to the next Fn. 
	 * Note that all but leftmost function will get 1 argument. If any of the functions returns null, subsequent functions are not called and null is
	 * returned.
	 * 
	 */	
	public static final Fn nncomp = new NonNullCompose();
	private static class NonNullCompose extends Fn {
		private NonNullCompose() {
			super(1);
		}
		@Override
		public Fn doFunc(Object... args) {
			final IFn[] functions = new IFn[args.length];
			for (int i = 0;i < args.length;i++) {
				functions[i] = (IFn)args[i];
			}
			class WorkerFn extends Fn {
				private IFn[] funs = functions;
				@Override
				public Object doFunc(Object... args) {
					Object ret = funs[0].invoke(args);
					for (IFn fn : funs) {
						if (ret == null) {
							return null;
						}
						ret = fn.invoke(ret);
					}
					return ret;
				}
				
			};
			return new WorkerFn();
		}
		
	}
	/**
	 * Returns a new function which is a composition of (one or more) argument functions. Functions are
	 * evaluated left to right, starting with leftmost function and using the result of the function call as an argument to the next Fn. 
	 * Note that all but leftmost function will get 1 argument. If any of the functions returns null, subsequent functions are not called and null is
	 * returned.
	 * 
	 * @param functions one or more Fns 
	 * @return 
	 */
	public static Fn nncomp(Object... functions) {
		return (Fn)nncomp.invoke(functions);
	}	
	/**
	 * Returns a new function which is a composition of (one or more) argument functions. Functions are
	 * evaluated left to right, starting with leftmost function and using the result of the function call as an argument to the next Fn. 
	 * Note that all but leftmost function will get 1 argument. If any of the functions returns null, subsequent functions are not called and null is
	 * returned.
	 * 
	 * @param functions one or more Fns 
	 * @return 
	 */
	public static Fn $NC(Object... functions) {
		return (Fn)nncomp.invoke(functions);
	}	

	////////////////////////////////////////////////////////////////////////////////
	// NOT
	////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Returns a new Fn which for any arguments returns logical negation (as per nil value definition) of whatever the sole argument(a Fn) would return.
	 */	
	public static final Fn not = new Not();
	private static class Not extends Fn {
		private Not() {
			super(1,1);
		}
		@Override
		public Fn doFunc(Object... args) {
			assert args[0] instanceof IFn : "Parameter of Not is not a IFn";
			final IFn targetFn = (IFn)args[0];
			class WorkerFn extends Fn {
				@Override
				public Object doFunc(Object... args) {
					return invertResult(targetFn.invoke(args));
				}
			};
			return new WorkerFn();
		}
		
	}
	/**
	 * Returns a new function which for any arguments returns logical negation (as per nil value definition) of whatever the target function would return.
	 * @param targetFn 
	 * @return a Fn
	 */
	public static Fn not(Object targetFn) {
		return (Fn)not.invoke(targetFn);
	}
    private static Object invertResult(Object result) {
        if (result == null) {
            return new Boolean(true);
        } else {
            if (result instanceof Boolean) {
                if ((Boolean)result) {
                    return new Boolean(false);
                } else {
                    return new Boolean(true);
                }
            } else {
                return null;
            }
        }                
    }
    	
	
	////////////////////////////////////////////////////////////////////////////////
	// PARTIAL
	////////////////////////////////////////////////////////////////////////////////

	public static final Sub _1 = new Sub(0);
	public static final Sub _2 = new Sub(1);
	public static final Sub _3 = new Sub(2);
	public static final Sub _4 = new Sub(3);
	public static final Sub _5 = new Sub(4);
	public static final Sub _6 = new Sub(5);
	public static final Sub _7 = new Sub(6);
	public static final Sub _8 = new Sub(7);
	public static final Sub _9 = new Sub(8);
	public static final Sub _10 = new Sub(9);
	public static final Sub _11 = new Sub(10);
	public static final Sub _12 = new Sub(11);
	public static final Sub _13 = new Sub(12);
	public static final Sub _14 = new Sub(13);
	public static final Sub _15 = new Sub(14);
	public static final Sub _16 = new Sub(15);
	public static final Sub _17 = new Sub(16);
	public static final Sub _18 = new Sub(17);
	public static final Sub _19 = new Sub(18);
	public static final Fn fn = new Partial();
	private static class Partial extends Fn {
		private Partial() {
			super(2);
		}
		@Override
		public Fn doFunc(Object... args) {
			boolean fnIsDelayedValue = false;			
			final Object targetFnOrDelayed = args[0];
			// contains delayed values that need to be calculated each invocation
			// they are positioned the same as they were in the original argument list
			// minus the first element which is the function
			final DelayedValue[] delayed = new DelayedValue[args.length-1];
			// holds the arguments used to invoke the inner function
			// most elements don't change, only delayed values will be swapped in each
			// invocation (if it has any)
			final Object[] argsList = new Object[args.length - 1];
			boolean hasDelayedValues = false; 

			// fill up argslist and remember the delayed values
			for (int i = 0;i < argsList.length;i++) {
				argsList[i] = args[i+1];			
				if (args[i+1] instanceof DelayedValue) {
					delayed[i] = (DelayedValue)args[i+1];
					hasDelayedValues = true;
				} 
			}
			if (args[0] instanceof DelayedValue) {
				fnIsDelayedValue = true;
			}
			if (hasDelayedValues) {
				if (fnIsDelayedValue) {
					// calculate delayed values and insert them into argsList, then
					// call the function using the argsList
					class WorkerFnDelayedArgsDelayed extends Fn {
						private final DelayedValue targetFn = (DelayedValue)targetFnOrDelayed;
						@Override
						public Object doFunc(Object... args) {
							for (int i = 0;i < delayed.length;i++) {
								if (delayed[i] != null) {
									argsList[i] = delayed[i].getValue(args); 
								}
							}
							return ((IFn)targetFn.getValue(args)).invoke(argsList);
						}
					};
					return new WorkerFnDelayedArgsDelayed();					
				} else {
					// calculate delayed values and insert them into argsList, then
					// call the function using the argsList
					class WorkerArgsDelayed extends Fn {
						private final IFn targetFn = (IFn)targetFnOrDelayed;
						@Override
						public Object doFunc(Object... args) {
							for (int i = 0;i < delayed.length;i++) {
								if (delayed[i] != null) {
									argsList[i] = delayed[i].getValue(args); 
								}
							}
							return targetFn.invoke(argsList);
						}
					};
					return new WorkerArgsDelayed();
				}
			} else {
				if (fnIsDelayedValue) {
					// have no substitutions
					// in this case use all supplied fixed arguments in inner function call and add all parameters 
					// of the wrapper to the call
					class WorkerFnDelayed extends Fn {
						final DelayedValue targetFn = (DelayedValue)targetFnOrDelayed;
						@Override
						protected Object doFunc(Object... args) {
							return ((IFn)targetFn.getValue(args)).invoke(argsList);
						}
					};
					return new WorkerFnDelayed();					
				} else {
					// have no substitutions
					// in this case use all supplied fixed arguments in inner function call and add all parameters 
					// of the wrapper to the call
					class Worker extends Fn {
						final IFn targetFn = (IFn)targetFnOrDelayed;
						@Override
						protected Object doFunc(Object... args) {
							Object[] combinedArgs = new Object[argsList.length + args.length];
							System.arraycopy(argsList, 0, combinedArgs, 0, argsList.length);
							System.arraycopy(args, 0, combinedArgs, argsList.length, args.length);
							return targetFn.invoke(combinedArgs);
						}
					};
					return new Worker();
				}
			}
		}
		
	}
	/**
	 * Function returns a new function which for any arguments returns logical negation of whatever the target function would return
	 * @param targetFn 
	 * @return 
	 */
	public static Fn fn(Object... fnAndArgs) {
		return (Fn) fn.invoke(fnAndArgs);
	}
	private static abstract class DelayedValue {
		protected abstract Object getValue(Object[] args);
	}
	private static class Delayed extends DelayedValue {
		private final IFn targetFn;
		private final boolean fnIsDelayed;
		private final DelayedValue fnDelayed;
		// contains delayed values that need to be calculated each invocation
		// they are positioned the same as they were in the original argument list
		// minus the first element which is the function
		private final DelayedValue[] delayed;
		// holds the arguments used to invoke the inner function
		// most elements don't change, only delayed values will be swapped in each
		// invocation (if it has any)
		private final Object[] argsList;
		private boolean hasDelayedValues = false; 
		private Delayed(Object... delayedInvocParams) {
			if (delayedInvocParams[0] instanceof DelayedValue) {
				fnIsDelayed = true;
				targetFn = null;
				fnDelayed = (DelayedValue)delayedInvocParams[0];
			} else {
				targetFn = (IFn)delayedInvocParams[0];
				fnIsDelayed = false;
				fnDelayed = null;
			}
			delayed = new DelayedValue[delayedInvocParams.length-1];
			argsList = new Object[delayedInvocParams.length - 1];
			// fill up argslist and remember the delayed values
			for (int i = 0;i < argsList.length;i++) {
				argsList[i] = delayedInvocParams[i+1];			
				if (delayedInvocParams[i+1] instanceof DelayedValue) {
					delayed[i] = (DelayedValue)delayedInvocParams[i+1];
					hasDelayedValues = true;
				} 
			}
		}
		@Override
		protected Object getValue(Object[] args) {
			if (hasDelayedValues) {
				for (int i = 0;i < delayed.length;i++) {
					if (delayed[i] != null) {
						argsList[i] = delayed[i].getValue(args); 
					}
				}
			}
			if (fnIsDelayed) {
				return ((IFn)fnDelayed.getValue(args)).invoke(argsList);
			} else {
				return targetFn.invoke(argsList);				
			}
		}
		
	}
	public static DelayedValue $(Object... fnAndArgs) {
		return new Delayed(fnAndArgs);
	}
	public static class Sub extends DelayedValue {
		private Sub(int pos) {
			position = pos;
		}
		private int position;
		@Override
		protected Object getValue(Object[] args) {
			if (position >= args.length) {
				throw new RuntimeException("Too few arguments to compute the substitute " +  
						(position + 1) + ", number of arguments is " + args.length);				
			} else {
				return args[position];
			}
		}
		
	}
    
    ////////////////////////////////////////////////////////////////////////////////
    // ANY
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Takes a predicate (a fn) and an iterable.
     * Returns a Boolean; true if any object in the iterable makes the predicate return a non-nil value.
     */
    public static final Fn any = new Any(); 
    private static class Any extends Fn {
        private Any() {
            super(2,2);
        }
        @Override
        public Boolean doFunc(Object... args) {
            final Iterable<Object> coll = (Iterable<Object>)args[1];
            final IFn predicate = (IFn)args[0];
            for (Object o : coll) {
                if (!isNil(predicate.invoke(o))) {
                    return true;
                }
            }
            return false;
        }
        
    }
    /**
     * Returns true if any object in the iterable makes the predicate return a non-nil value.
     * @param predicate a Fn
     * @param iter an iterable 
     * @return a Boolean; true if predicate returns non-nil for every value, false otherwise
     */
    public static Boolean any(Object predicate, Object iter) {
        return (Boolean)any.invoke(predicate, iter);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // CONCAT
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Function concatenates iterables. If it has 1 parameter and it is iterable, 
     * the function checks if it is iterable of iterables and concatenates subiterables and subelements.
     * Otherwise it concatenates supplied values and iterables into one iterable.
     * It will not flatten data structures with deeper nesting.
     * 
     */ 
    public static final Fn concat = new Concat();
    private static class Concat extends Fn {
        @Override
        public Object doFunc(Object... args) {
            if (args.length == 0) return new EmptyIterable<Object>();
            // concatenable elements
            final Object[] elements = args;
            class ResIter extends SingleCheckIterator<Object> {
                ResIter(Object[] els) {
                    if (els != null && els.length == 1 && els[0] instanceof Iterable<?>) {
                        outerIter = ((Iterable<Object>)elements[0]).iterator();
                    } else {
                        argIndex = -1;
                    }
                }
                // multi param mode
                private int argIndex = -1;
                // single param mode
                Iterator<Object> outerIter;
                // are we returning elements of an iterable and that iterable is
                Iterator<Object> innerIter = null;
                Object nextElement = null;

                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(elements);
                }
                @Override
                protected boolean existsNext() {
                    // if we are returning elements from an iterable
                    // and that iterable has more, then we have more elements
                    if (innerIter != null) {
                        if (innerIter.hasNext()) {
                            nextElement = innerIter.next();
                            return true;
                        } else { // we exited an inner iterator
                            innerIter = null;
                        }
                    } 
                    if (outerIter == null) { // so we have a series of objects as parameter
                        for (int i = argIndex+1;i < elements.length;i++) { // look for the next object
                            if (elements[i] instanceof Iterable) { // next object an iterable?
                                innerIter = ((Iterable<Object>)elements[i]).iterator();
                                if (innerIter.hasNext()) { // check if it has anything in it
                                    argIndex = i; // and return that, also mark it for being inner iterator
                                    nextElement = innerIter.next();
                                    return true;
                                } // if iterator was empty continue with the loop
                            } else { // not an iterator, we just return it
                                nextElement = elements[i];
                                argIndex = i;
                                return true;
                            }
                        }
                        outerIter = null; // make sure iterators and elements get garbage collected
                        innerIter = null;
                        nextElement = null;
                        return false; // haven't found anything
                    } else { // we have a single paramter - iterable
                        while (outerIter.hasNext()) { // scroll though outer iterable
                            Object el = outerIter.next(); 
                            if (el instanceof Iterable) { // next object an iterable?
                                innerIter = ((Iterable<Object>)el).iterator();
                                if (innerIter.hasNext()) { // check if it has anything in it
                                    nextElement = innerIter.next();
                                    return true;
                                } // if iterator was empty continue with the loop
                            } else { // not an iterator, we just return it
                                nextElement = el;
                                return true;
                            }
                        }
                        outerIter = null; // make sure iterators get garbage collected
                        innerIter = null;
                        nextElement = null;
                        return false;
                    }
                }

                @Override
                protected Object generateNext() {
                    return nextElement;
                }

            }
            return new ResIter(null);
        }       
    }
    /**
     * Function concatenates iterables. If it has 1 parameter and it is iterable, 
     * the function checks if it is iterable of iterables and concatenates subiterables and subelements.
     * Otherwise it concatenates supplied values and iterables into one iterable.
     * It will not flatten data structures with deeper nesting.
     * 
     * @param data either an iterable of iterables or a combination of non-iterable and iterable parameters  
     * @return single iterable containing all data.
     *
     */
    public static <T> Iterable<T> concat(Object... data) {
        return (Iterable<T>)concat.invoke(data);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // DOALL
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Method which iterats through an iterable and returns the iterable.
     * Used to realize side-effects in lazy iterables.
     * 
     * @param iter
     * @return
     */
    public static <T> Iterable<T> doAll(Iterable<T> iter) {
       Iterator<T> i = iter.iterator();
       while(i.hasNext()) {
           i.next();
       }
       return iter;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // DISTINCT
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns new Iterable without duplicated elements. All but the first occurrence of an element are removed.
     * Takes an iterable and an optional fn. If a fn is provided then equality of its return value on iterable's elements is used
     * instead of natural equality to determine duplicates.
     * 
     */ 
    public static final Fn distinct = new Distinct();
    private static class Distinct extends Fn {
        private Distinct() {
            super(1);
        }
        @Override
        public Iterable<? extends Object> doFunc(Object... args) {
            final Iterable<Object> coll = (Iterable<Object>)args[args.length-1];;
            class ResIterDistinctNatural extends SingleCheckIterator<Object> {
                private HashSet<Object> set;
                private Iterator<Object> iter;
                ResIterDistinctNatural(Iterable<Object> coll) {
                    if (coll != null) {
                        if (coll instanceof Collection) {
                            set = new HashSet<Object>(((Collection<?>)coll).size());
                        } else {
                            set = new HashSet<Object>();
                        }
                        iter = coll.iterator();
                    } else {
                        set = null;
                        iter = null;
                    }
                }
                private Object nextRet = null;
                @Override
                protected boolean existsNext() {
                    while (iter.hasNext()) {
                        nextRet = iter.next();
                        if (set.add(nextRet)) {
                            return true;
                        }
                    }
                    nextRet = null; // make sure intermediate objects get garbage collected               
                    set = null; 
                    iter = null;
                    return false;
                }

                @Override
                protected Object generateNext() {
                    return nextRet;
                }

                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIterDistinctNatural(coll);
                }
                
            };
            class ResIterDistinctSingle extends SingleCheckIterator<Object> {
                private HashSet<Object> set;
                private Iterator<Object> iter;
                private IFn fn;
                ResIterDistinctSingle(Iterable<Object> coll, IFn fn) {
                    this.fn = fn;
                    if (coll != null) {
                        if (coll instanceof Collection) {
                            set = new HashSet<Object>(((Collection<?>)coll).size());
                        } else {
                            set = new HashSet<Object>();
                        }
                        iter = coll.iterator();
                    } else {
                        set = null;
                        iter = null;
                    }
                }
                private Object nextRet = null;
                @Override
                protected boolean existsNext() {
                    while (iter.hasNext()) {
                        nextRet = iter.next();
                        if (set.add(fn.invoke(nextRet))) {
                            return true;
                        }
                    }
                    nextRet = null; // make sure intermediate objects get garbage collected               
                    set = null; 
                    iter = null;                    
                    return false;
                }

                @Override
                protected Object generateNext() {
                    return nextRet;
                }

                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIterDistinctSingle(coll, fn);
                }
                
            };
            class ResIterDistinctMulti extends SingleCheckIterator<Object> {
                private HashSet<Object> set;
                private Iterator<Object> iter;
                private IFn[] fns;
                private Object[] fnResults;
                ResIterDistinctMulti(Iterable<Object> coll, IFn[] fns) {
                    this.fns = fns;
                    this.fnResults = new Object[fns.length];
                    if (coll != null) {
                        if (coll instanceof Collection) {
                            set = new HashSet<Object>(((Collection<?>)coll).size());
                        } else {
                            set = new HashSet<Object>();
                        }
                        iter = coll.iterator();
                    } else {
                        set = null;
                        iter = null;
                    }
                }
                private Object nextRet = null;
                @Override
                protected boolean existsNext() {
                    while (iter.hasNext()) {
                        nextRet = iter.next();
                        // at this point we are going to assume that fnResults
                        // is a new instance, not already present in the HashSet
                        // instanced either in constructor or in this method before the
                        // last unique element was calculated, allowing us to skip array
                        // allocation for non-unique elements
                        for (int i = 0;i < fns.length;i++) {
                            fnResults[i] = fns[i].invoke(nextRet);
                        }
                        if (set.add(Arrays.asList(fnResults))) {
                            // now we allocate a new fnResult array so HashSet uniqueness guaranteed doesn't
                            // get ruined by mutating the elements already in it
                            fnResults = new Object[fnResults.length];
                            return true;
                        }
                    }
                    nextRet = null; // make sure intermediate objects get garbage collected               
                    set = null; 
                    iter = null; 
                    fnResults = null;
                    return false;
                }

                @Override
                protected Object generateNext() {
                    return nextRet;
                }

                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIterDistinctMulti(coll, fns);
                }
                
            };    
            if (args.length == 1) {
                return new ResIterDistinctNatural(null);
            } else if (args.length == 2) {
                return new ResIterDistinctSingle(null, (IFn)args[0]);
            } else {
                IFn[] fns = new IFn[args.length-1];
                System.arraycopy(args, 0, fns, 0, fns.length);
                return new ResIterDistinctMulti(null, fns);                       
            }
        }       
    }
    /**
     * Returns new Iterable without duplicated elements. All but the first occurrence of an element are removed.
     * Takes a variable number of fns and an iterable. 
     * If no functions is are given (1 parameter) then natural equality is used.
     * Otherwise elements are considered equal when the tuplet of function returns is equal.
     * 
     * @param compFnsAndIter functions and a single iterable (iterable is non-optional)
     * @return Iterable
     */
    public static <T> Iterable<T> distinct(Object... compFnsAndIter) {
        return (Iterable<T>)distinct.invoke(compFnsAndIter);
    }    

    ////////////////////////////////////////////////////////////////////////////////
    // DROP
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns an iterable which contains elements of the supplied iterable without the first num ones. 
     * First parameter is number of elements to drop from the source iterable (between 0 and max long).
     * Second parameter is an iterable.
     */ 
    public static final Fn drop = new Drop();
    private static class Drop extends Fn {
        private Drop() {
            super(2,2);
        }
        @Override
        public Iterable<? extends Object> doFunc(Object... args) {
            final Iterable<Object> iterable = (Iterable<Object>)args[1];
            final Long num = ((Number)args[0]).longValue();
            class ResIter extends SingleCheckIterator<Object> {
                private long idx = 0;
                private Iterator<Object> iter;
                ResIter(Iterable<Object> itr) {
                    if (itr != null) {
                        iter = itr.iterator();
                    } else {
                        iter = null;
                    }
                }
                @Override
                protected boolean existsNext() {
                    while(idx < num) {
                        if (iter.hasNext()) {
                            iter.next();
                            idx++;
                        } else {
                            iter = null; // allow garbage collection for iterator
                            return false;
                        }
                    }
                    if (iter.hasNext()) {
                        return true;
                    } else {
                        iter = null; // allow garbage collection for iterator
                        return false;                        
                    }
                }
                @Override
                protected Object generateNext() {
                    return iter.next();
                }
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(iterable);
                }   
            }
            return new ResIter(null);
        }
        
    }
    
    /**
     * Returns an iterable which contains elements of the supplied iterable without the first num ones. 
     * @param num number of elements to drop from the source iterable (between 0 and max long)
     * @param iter  
     * @return 
     */
    public static <T> Iterable<T> drop(Object num, Object iter) {
        return (Iterable<T>)drop.invoke(num, iter);
    }   

    ////////////////////////////////////////////////////////////////////////////////
    // DROPWHILE
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns an iterable which contains elements of the supplied iterable without the first few which 
     * fit the supplied fn i.e. it keeps dropping elements as long as predicate is true. Where predicate 
     * returns nil, the returned iterable starts (even though later elements might make it non-nil again) 
     * and it runs till the end of the parameter iterable.
     * 
     * First parameter is the predicate fn, second one is an iterable.
     */ 
    public static final Fn dropWhile = new DropWhile();
    private static class DropWhile extends Fn { 
        private DropWhile() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            final Iterable<Object> iterable = (Iterable<Object>)args[1];
            final IFn pred = (IFn)args[0];
            class ResIter extends SingleCheckIterator<Object> {
                private Iterator<Object> iter;
                private boolean started = false;
                private boolean firstElement = true;
                Object nextRet = null;
                ResIter(Iterable<Object> itr) {
                    if (itr != null) {
                        iter = itr.iterator();
                    } else {
                        iter = null;
                    }
                }
                @Override
                protected boolean existsNext() {
                    if (!started) {
                        while (iter.hasNext()) {
                            nextRet = iter.next();
                            if (isNil(pred.invoke(nextRet))) {
                                started = true;
                                return true;
                            } 
                        } 
                        nextRet = null; // allow for garbage collection of temporary objects
                        iter = null;
                        return false;
                    }
                    if (iter.hasNext()) {
                        return true;
                    } else {
                        nextRet = null;  // allow for garbage collection of temporary objects
                        iter = null;
                        return false;                        
                    }
                }
                @Override
                protected Object generateNext() {
                    if (firstElement) {
                        firstElement = false;
                        return nextRet;
                    } else {
                        return iter.next();
                    }
                }
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(iterable);
                }   
            }
            return new ResIter(null);
        }
        
    }   
    /**
     * Returns an iterable which contains elements of the supplied iterable without the first few which 
     * fit the supplied fn i.e. it keeps dropping elements as long as predicate is true. Where predicate 
     * returns nil, the returned iterable starts (even though later elements might make it non-nil again) 
     * and it runs till the end of the parameter iterable.
     * 
     * @param predicate  
     * @param iter  
     * @return 
     */
    
    public static <T> Iterable<T> dropWhile(Object predicate, Object iter) {
        return (Iterable<T>)dropWhile.invoke(predicate, iter);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // EVERY
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns true if every object in iterable makes the predicate fn return a non-nil value.
     * First parameter is the predicate fn, second parameter is an iterable.
     * 
     */
    public static final Fn every = new Every();
    private static class Every extends Fn {
        private Every() {
            super(2,2);
        }
        @Override
        public Boolean doFunc(Object... args) {
            final Iterable<Object> coll = (Iterable<Object>)args[1];
            final IFn predicate = (IFn)args[0];
            for (Object o : coll) {
                if (isNil(predicate.invoke(o))) {
                    return false;
                }
            }
            return true;
        }
        
    }
    /**
     * Returns true if every object in iterable makes the predicate fn return a non-nil value.
     * @param predicate 
     * @param iter  
     * @return true if predicate returns non-nil for every value
     */
    public static Boolean every(Object predicate, Object iter) {
        return (Boolean)every.invoke(predicate, iter);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // FILTER
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns new iterable without elements for which predicate fn returns nil.
     * First parameter is a fn used to filter.
     * Second parameter is an iterable.
     * Returns a new iterable.
     * 
     */
    public static final Fn filter = new Filter();
    private static class Filter extends Fn {
        private Filter() {
            super(2,2);
        }
        @Override
        public Iterable<? extends Object> doFunc(Object... args) {
            final Iterable<Object> coll = (Iterable<Object>)args[1];
            final IFn filterFn = (IFn)args[0];
            class ResIter extends SingleCheckIterator<Object> {
                private Iterator<Object> iter = coll.iterator();
                private Object nextRet = null;
                ResIter(Iterable<Object> itr) {
                    if (itr != null) {
                        iter = itr.iterator();
                    } else {
                        iter = null;
                    }
                }
                @Override
                protected boolean existsNext() {
                    while (iter.hasNext()) {
                        nextRet = iter.next();
                        if (!isNil((filterFn.invoke(nextRet)))) {
                            return true;
                        }
                    }
                    iter = null; // allow for garbage collection of temporary objects
                    nextRet = null;
                    return false;
                }

                @Override
                protected Object generateNext() {
                    return nextRet;
                }

                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(coll);
                }
                
            }
            return new ResIter(null);
        }       
    }
    
    /**
     * Returns new iterable without elements for which predicate fn returns nil.
     * @param predicate fn used to filter 
     * @param iter Iterable 
     * @return Iterable
     */ 
    public static <T> Iterable<T> filter(Object predicate, Object iter) {
        return (Iterable<T>)filter.invoke(predicate, iter);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // GROUPBY
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Takes an iterable and a fn that can take 1 argument and returns a map of lists which contain the elements of the original iterable
     * grouped by fn value of elements. Return value of the fn on an element is the map key.
     *
     * E.g: suppose 'firstLetter' is a fn which returns first letter of the string 
     * ["alaska" "attribute" "bomb" "failure" "Form"] -> groupBy(firstLetter, coll) ->
     * { "a" -> ["alaska" "attribute"], "b" -> ["bomb"], "f" -> ["failure"], "F" -> ["Form"] }
     * Map entries are organized by the type of map used (default HashMap has random order of keys)
     *
     * First parameter is the fn.
     * Second parameter is an iterable.
     * Returns a map of object to list of objects 
     */
    public static final Fn groupBy = new GroupBy();
    private static class GroupBy extends Fn {
        private GroupBy() {
            super(2,2);
        }
        @Override
        public Map<Object, List<Object>> doFunc(Object... args) {
            Map<Object, List<Object>> retVal = CollectionFactory.activeCollectionFactory.getMapInstance();
            IFn function = (IFn)args[0];
            // this object will surely not be equal to anything else            
            Object groupingValue = null;
            List<Object> elemsWithThisResult = null; 
            for (Object elem : (Iterable<Object>)args[1]) {
                groupingValue = function.invoke(elem);
                elemsWithThisResult = retVal.get(groupingValue);
                if (elemsWithThisResult == null) {
                    elemsWithThisResult = CollectionFactory.activeCollectionFactory.getListInstance();
                    retVal.put(groupingValue, elemsWithThisResult);
                }
                elemsWithThisResult.add(elem);
            }
            return retVal;
        }
        
    }
    /**
     * Takes an iterable and a fn that can take 1 argument and returns a map of lists which contain the elements of the original iterable
     * grouped by fn value of elements. Return value of the fn on an element is the map key.
     *
     * E.g: suppose 'firstLetter' is a fn which returns first letter of the string 
     * ["alaska" "attribute" "bomb" "failure" "Form"] -> groupBy(firstLetter, coll) ->
     * { "a" -> ["alaska" "attribute"], "b" -> ["bomb"], "f" -> ["failure"], "F" -> ["Form"] }
     * Map entries are organized by the type of map used (default HashMap has random order of keys)
     * 
     * @param fun Function of 1 argument. The return value is used as a map key
     * @param iterable
     * @return a map of object to list of objects 
     */
    public static <T,V> Map<T, List<V>> groupBy(Object fun, Object iterable) {
        return (Map<T, List<V>>)groupBy.invoke(fun, iterable);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // GROUPBYSORTED
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn groupBySorted = new GroupBySorted();
    private static class GroupBySorted extends Fn {
        private GroupBySorted() {
            super(4,4);
        }
        @Override
        public Object doFunc(Object... args) {
            IFn function = (IFn)args[0];
            Iterable<Object> iter = (Iterable<Object>)args[3];
            Comparator<Object> listComparator = (Comparator<Object>)args[2];
            Comparator<Object> keyComparator = (Comparator<Object>)args[1];
            Map<Object, List<Object>> retVal;
            if (keyComparator != null) {
                retVal = CollectionFactory.activeCollectionFactory.getSortedMapInstance(keyComparator);
            } else {
                retVal = CollectionFactory.activeCollectionFactory.getMapInstance();
            }           
            // this object will surely not be equal to anything else            
            Object groupingValue = null;
            List<Object> elemsWithThisResult = null; 
            for (Object elem : iter) {
                groupingValue = function.invoke(elem);
                elemsWithThisResult = retVal.get(groupingValue);
                if (elemsWithThisResult == null) {
                    elemsWithThisResult = CollectionFactory.activeCollectionFactory.getListInstance();
                    retVal.put(groupingValue, elemsWithThisResult);
                }
                if (listComparator != null) {
                    int result = Collections.binarySearch(elemsWithThisResult, elem, listComparator);
                    if (result < 0) {
                        elemsWithThisResult.add(-result, elem); 
                    } else {
                        int i = result + 1;
                        while(i < elemsWithThisResult.size()) {
                            if (listComparator.compare(elem, elemsWithThisResult.get(i)) != 0) {
                                break;
                            }
                            i++;
                        }
                        elemsWithThisResult.add(i, elem);
                    }
                } else {
                    elemsWithThisResult.add(elem);
                }
            }
            return retVal;
        }
        
    }
    /**
     * Takes an iterable and returns a map of lists which contain the elements of the original iterable
     * grouped by function value of elements. 
     * Values in lists (which are a value in the map) are sorted by valueComparator by using binary insert.
     * If keyComparator is not null then returned Map is a SortedMap, where keys are sorted using
     * the key comparator.
     * 
     * @param fun Function of one argument, returns the object for the element to be sorted by
     * @param comparator
     * @param iterable
     * @return 
     */
    public static <T,V> Map<T, List<V>> groupBySorted(Object fun, Object keyComparator, Object valueComparator, Object iterable) {
        return (Map<T, List<V>>)groupBySorted.invoke(fun, keyComparator, valueComparator, iterable);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // INTERLEAVE
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn interleave = new Interleave();
    private static class Interleave extends Fn {    
        private Interleave() {
            super(2);
        }
        @Override
        public Object doFunc(Object... args) {
            final Iterable<Object>[] iterables = new Iterable[args.length];
            for (int i = 0;i < args.length;i++) {
                iterables[i] = (Iterable<Object>)args[i];
            }
            class ResIter extends SingleCheckIterator<Object> {
                private boolean finished = false;
                private int nextIter = 0;
                private Iterator<Object>[] iterators;
                private ResIter(Iterable<Object>[] iters) {
                    if (iters != null) {
                        iterators = new Iterator[iters.length];
                        for (int i = 0;i < iters.length;i++) {
                            iterators[i] = iters[i].iterator();
                        }
                    } else {
                        iterators = null;
                    }
                }
                @Override
                protected boolean existsNext() {
                    if (finished) {
                        return false;
                    }
                    if (nextIter == 0) {
                        for (Iterator<Object> iter : iterators) {
                            if (!iter.hasNext()) {
                                finished = true;
                                iterators = null; // allow for garbage collection of temporary objects
                                return false;
                            }
                        }
                    }
                    return true;
                }

                @Override
                protected Object generateNext() {
                    if (++nextIter == iterators.length) {
                        nextIter = 0;
                        return iterators[iterators.length - 1].next();
                    } else {
                        return iterators[nextIter - 1].next();
                    }
                }

                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(iterables);
                }
                
            }
            return new ResIter(null);
        }
        
    }
    /**
     * Returns new iterable which interleaves parameter iterables - It cycles the iterables
     * taking one element from each. The shortest parameter iterable determines the length of the resulting
     * iterable. Thus parameters with lengths 4 , 3 , 5 , 7 will produce a sequence of length: 
     * 3 (shortest) * 4 (number of iterables) = 12 elements.
     * @param iterables 
     * @return Iterable
     */
    
    public static <T> Iterable<T> interleave(Object... iterables) {
        return (Iterable<T>)interleave.invoke(iterables);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // INTERPOSE
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn interpose = new Interpose();
    private static class Interpose extends Fn {
        private Interpose() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            final Iterable<Object> coll = (Iterable<Object>)args[1];
            final Object separator = args[0];
            class ResIter extends SingleCheckIterator<Object> {
                private Iterator<Object> iter;
                private Object nextRet = null;
                private boolean sep = false;
                ResIter(Iterable<Object> itr) {
                    if (itr != null) {
                        iter = itr.iterator();
                    } else {
                        iter = null;
                    }
                }
                @Override
                protected boolean existsNext() {
                    if (iter.hasNext()) {
                        if (sep) {
                            nextRet = separator;
                        } else {
                            nextRet = iter.next();
                        }
                        sep = !sep;
                        return true;
                    }
                    iter = null; // allow for garbage collection of temporary objects
                    nextRet = null;        
                    return false;
                }

                @Override
                protected Object generateNext() {
                    return nextRet;
                }

                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(coll);
                }
                
            }
            return new ResIter(null);
        }
        
    }
    /**
     * Returns new iterable with separator between elements of the original iterable
     * @param separator separator object 
     * @param iter Iterable 
     * @return Iterable
     */
    
    public static <T> Iterable<T> interpose(Object separator, Object iter) {
        return (Iterable<T>)interpose.invoke(separator, iter);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // INTO
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn into = new Into();
    private static class Into extends Fn {
        private Into() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            if (args[1] == null) {
                return args[0];
            }
            if (args[0] instanceof java.util.Map) {
                java.util.Map<Object, Object> coll = (java.util.Map<Object, Object>)args[0];
                Iterator<Object> iter = ((Iterable<Object>)args[1]).iterator();
                while(iter.hasNext()) {
                    coll.put(iter.next(), iter.next());
                }
                return coll;
            } else { // assume collection
                Collection<Object> coll = (Collection<Object>)args[0];
                for (Object elem : (Iterable<Object>)args[1]) {
                    coll.add(elem);
                }
                return coll;
            }
        }
        
    }
    /**
     * Inserts elements of from parameter into to parameter, modifying the to paramter collection.
     * @param to - A collection or map. NOT JUST ITERABLE
     * @param from An iterable 
     * @return 
     */
    public static <T> T into(Object to, Object from) {
        return (T)into.invoke(to, from);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // ITERATE
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns new infiniter iterable which returns a sequence of 
     * [startObject, fn(startObject), fn(fn(startObject)), fn(fn(fn(startObject))), ...]
     * First parameter is a function of 1 parameter 
     * Second parameter the object to start with 
     * Returns Iterable
     */ 
    public static final Fn iterate = new Iterate();
    private static class Iterate extends Fn {
        private Iterate() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            final Object starterObject = args[1];
            final IFn function = (IFn)args[0];
            class ResIter extends SingleCheckIterator<Object> {
                private Object currentObj = starterObject;
                @Override
                protected boolean existsNext() {
                    return true;
                }

                @Override
                protected Object generateNext() {
                    Object nextRet = currentObj;
                    currentObj = function.invoke(nextRet);
                    return nextRet;
                }

                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter();
                }
                
            }
            return new ResIter();
        }
        
    }
    /**
     * Returns new infiniter iterable which returns a sequence of 
     * [startObject, fn(startObject), fn(fn(startObject)), fn(fn(fn(startObject))), ...]
     * @param fn a function of 1 parameter 
     * @param startObject the object to start with 
     * @return Iterable
     */ 
    public static <T> Iterable<T> iterate(Object fn, Object startObject) {
        return (Iterable<T>)iterate.invoke(fn, startObject);
    }   
    ////////////////////////////////////////////////////////////////////////////////
    // LIST
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn list = new ListFn();
    private static class ListFn extends Fn {
        private ListFn() {
            super(1,2);
        }
        @Override
        public Object doFunc(Object... args) {
            List<Object> retList = CollectionFactory.activeCollectionFactory.getListInstance();
            if (args[0] == null) {
                return retList;
            }
            if (args[0] instanceof Map) {
                for (Entry<?, ?> elem : ((Map<?,?>)args[0]).entrySet()) {
                    retList.add($V(elem.getKey(), elem.getValue()));
                }               
            } else {
                if (args.length == 2 && (Boolean)args[1]) {
                    for (Object elem : (Iterable<Object>)args[0]) {
                        if ((elem instanceof Iterable) && !(elem instanceof Collection)) {
                            elem = $L(elem);
                        }
                        retList.add(elem);
                    }                   
                } else {
                    for (Object elem : (Iterable<Object>)args[0]) {
                        retList.add(elem);
                    }                                       
                }
            }
            return retList;
        }
        
    }
    
    /**
     * Returns new List containing the elements of the iterable. 
     * @param elements
     * @return 
     */
    public static <T> List<T> $L(Object iterable) {
        return (List<T>)list.invoke(iterable);
    }
    
    /**
     * Returns new List containing the elements of the iterable. Also converts nested iterables to lists
     * except those that are already Lists or Sets if the recurse parameter is true.
     * @param elements
     * @param recurse 
     * @return 
     */
    public static <T> List<T> $L(Object iterable, Object recurse) {
        return (List<T>)list.invoke(iterable, recurse);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // MAKEMAP
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn makeMap = new MakeMap();
    private static class MakeMap extends Fn {
        @Override
        protected Object doFunc(Object... args) {
            Map<Object,Object> retMap = CollectionFactory.activeCollectionFactory.getMapInstance(args.length / 2);
            if (args.length % 2 != 0) {
                throw new IllegalArgumentException("$M can't construct a map with odd number of parameters");               
            }
            for (int i = 0;i < args.length;i += 2) {
                retMap.put(args[i], args[i+1]);
            }           
            return retMap;
        }
    }
    /**
     * Returns new set containing the elements
     * @param elements
     * @param  
     * @return 
     */
    public static <T,V> Map<T, V> $M(Object... elements) {
        return (Map<T,V>)makeMap.invoke(elements);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // MAKESET
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn makeSet = new MakeSet();
    private static class MakeSet extends Fn {
        @Override
        protected Object doFunc(Object... args) {
            Set<Object> retSet = CollectionFactory.activeCollectionFactory.getSetInstance(args.length);
            for (Object o : args) {
                retSet.add(o);
            }
            return retSet;
        }
    }
    /**
     * Returns new set containing the elements
     * @param elements
     * @param  
     * @return 
     */
    public static <T> Set<T> $S(Object... elements) {
        return (Set<T>)makeSet.invoke(elements);
    }
    ////////////////////////////////////////////////////////////////////////////////
    // MAP
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn map = new MapFn();
    private static class MapFn extends Fn {
        private MapFn() {
            super(2);
        }
        @Override
        public Iterable<? extends Object> doFunc(final Object... args) {
            final IFn mapFn = (IFn)args[0];
            class ResIter extends SingleCheckIterator<Object>{
                private Iterator<?>[] colls;
                private Object[] params;
                private ResIter(final Object[] itrs) {                  
                    if (itrs != null) {
                        colls = new Iterator<?>[itrs.length-1];
                        for (int i = 1;i < itrs.length;i++) {
                            colls[i-1] = ((Iterable<?>)itrs[i]).iterator();
                        }
                        params = new Object[colls.length];
                    } else {
                        colls = null;
                        params = null;
                    }
                }
                @Override
                protected boolean existsNext() {
                    for (Iterator<?> iter : colls) {
                        if (!iter.hasNext()) {
                            colls = null; // allow for garbage collection of temporary objects
                            params = null;
                            return false;
                        }
                    }
                    return true;                    
                }
                @Override
                protected Object generateNext() {
                    for (int i = 0;i < colls.length;i++) {
                        params[i] = colls[i].next();
                    }
                    return mapFn.invoke(params);
                }
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(args);
                }
            }
            return new ResIter(null);
        }
        
    }

    /**
     * Returns new iterable when each element is the result of the function applied over elements of the iterables
     * 
     * @param predicate Function used to filter 
     * @return Iterable
     */
    
    public static <T> Iterable<T> map(Object... fnAndIterables) {
        return (Iterable<T>)map.invoke(fnAndIterables);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // MAPINDEXED
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn mapIndexed = new MapIndexed();
    private static class MapIndexed extends Fn {
        private MapIndexed() {
            super(2);
        }
        @Override
        public Object doFunc(final Object... args) {
            final IFn mapFn = (IFn)args[0];
            class ResIter extends SingleCheckIterator<Object>{
                private Iterator<?>[] colls;
                private long index = 0;
                private ResIter(final Object[] itrs) {                  
                    if (itrs != null) {
                        colls = new Iterator<?>[itrs.length]; 
                        for (int i = 1;i < itrs.length;i++) { // we will leave first spot null
                            colls[i] = ((Iterable<?>)itrs[i]).iterator();
                        }
                        params = new Object[1+colls.length];
                    } else {
                        colls = null;
                        params = null;
                    }
                }               

                @Override
                protected boolean existsNext() {  // allow for garbage collection of temporary objects
                    for (Iterator<?> iter : colls) {
                        if (!iter.hasNext()) {
                            // allow for garbage collection of temporary objects
                            params = null;
                            colls = null;
                            return false;
                        }
                    }
                    return true;                    
                }
                private Object[] params;
                @Override
                protected Object generateNext() {
                    params[0] = index;
                    for (int i = 1;i < colls.length;i++) {
                        params[i] = colls[i].next();
                        index++;
                    }
                    return mapFn.invoke(params);
                }
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(args);
                }
            }
            return new ResIter(null);
        }
        
    }

    /**
     * Returns new iterable when each element is the result of the function applied 
     * over the index of the element and elements of the iterables. 
     * 
     * @param predicate Function used to filter 
     * @return Iterable
     */
    
    public static <T> Iterable<T> mapIndexed(Object... fnAndIterables) {
        return (Iterable<T>)mapIndexed.invoke(fnAndIterables);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // MAX
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn max = new Max();
    private static class Max extends Fn {   
        private Max() {
            super(1,2);
        }
        @SuppressWarnings("rawtypes")
        @Override
        public Object doFunc(Object... args) {
            Object maxElem = null;
            Comparable maxValue = null;
            final Iterator<Object> iter = args.length == 2 ? ((Iterable<Object>)args[1]).iterator() : ((Iterable<Object>)args[0]).iterator();
            final IFn fn = args.length == 2 ? (IFn)args[0] : null;
            if (iter.hasNext()) {
                maxElem = iter.next();
                maxValue = fn == null ? (Comparable)maxElem : (Comparable)fn.invoke(maxElem);
            } else {
                return null;
            }
            Object currentElem;
            Comparable currentValue;
            while (iter.hasNext()) {
                currentElem = iter.next();
                currentValue = fn == null ? (Comparable)currentElem : (Comparable)fn.invoke(currentElem);
                if (currentValue.compareTo(maxValue) > 0) {
                    maxElem = currentElem;
                    maxValue = currentValue;
                }
            }
            return maxElem;
        }
        
    }
    /**
     * Returns maximum element of the iterable. Elements must implement Comparable 
     * @param iter  
     * @return 
     */
    public static <T> T max(Object iter) {
        return (T)max.invoke(iter);
    }
    /**
     * Returns maximum element of the iterable based on function result. 
     * Function result must implement Comparable
     * @param fun Function of one argument 
     * @param iter  
     * @return 
     */
    public static <T> T max(Object fun, Object iter) {
        return (T)max.invoke(iter);
    }
    ////////////////////////////////////////////////////////////////////////////////
    // MEMOIZEITER
    ////////////////////////////////////////////////////////////////////////////////
    /**
     * Returns an iterable which decorates the parameter iterable with memoization of
     * elements that have been returned. 
     *  
     * Single parameter: Iterable which you want to wrap.  
     * 
     */
    public static final Fn memoizeIter = new MemoizeIter();
    private static class MemoizeIter extends Fn {   
        private MemoizeIter() {
            super(1,1);
        }
        @Override
        public Object doFunc(Object... args) {
            final Iterable<Object> orig = (Iterable<Object>)args[0];
            class ResIter extends SingleCheckIterator<Object> {
                private int idx = 0;
                private Iterator<Object> baseIterator;
                private List<Object> memoizedElements;
                ResIter(Iterator<Object> itr) {
                    if (itr != null) {
                        baseIterator = itr ;
                        memoizedElements = new ArrayList<Object>();
                    } else {
                        baseIterator = null;
                    }
                }
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter(orig.iterator());
                }
                
                @Override
                protected boolean existsNext() {
                    if (idx < memoizedElements.size() || baseIterator.hasNext()) {
                        return true;
                    } else {
                        // allow for garbage collection of temporary objects
                        baseIterator = null;
                        return false;
                    }
                }

                @Override
                protected Object generateNext() {
                    if (idx < memoizedElements.size()) {
                        return memoizedElements.get(idx);
                    } else {
                        Object ret = baseIterator.next();
                        memoizedElements.add(ret);
                        idx++;
                        return ret;
                    }
                }
                
            }
            return new ResIter(null);
        }
        
    }
    /**
     * Returns an iterable which decorates the parameter iterable with memoization of
     * elements that have been returned. 
     *  
     * @param iter  
     * @return 
     */
    public static <T> Iterable<T> memoizeIter(Object iter) {
        return (Iterable<T>)memoizeIter.invoke(iter);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // MIN
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn min = new Min();
    private static class Min extends Fn {   
        private Min() {
            super(1,2);
        }
        @SuppressWarnings("rawtypes")
        @Override
        public Object doFunc(Object... args) {
            Object maxElem = null;
            Comparable maxValue = null;
            final Iterator<Object> iter = args.length == 2 ? ((Iterable<Object>)args[1]).iterator() : ((Iterable<Object>)args[0]).iterator();
            final IFn fn = args.length == 2 ? (IFn)args[0] : null;
            if (iter.hasNext()) {
                maxElem = iter.next();
                maxValue = fn == null ? (Comparable)maxElem : (Comparable)fn.invoke(maxElem);
            } else {
                return null;
            }
            Object currentElem;
            Comparable currentValue;
            while (iter.hasNext()) {
                currentElem = iter.next();
                currentValue = fn == null ? (Comparable)currentElem : (Comparable)fn.invoke(currentElem);
                if (currentValue.compareTo(maxValue) < 0) {
                    maxElem = currentElem;
                    maxValue = currentValue;
                }
            }
            return maxElem;
        }
        
    }
    /**
     * Returns minimum element of the iterable. Elements must implement Comparable 
     * @param iter  
     * @return 
     */
    public static <T> T min(Object iter) {
        return (T)min.invoke(iter);
    }
    /**
     * Returns minimum element of the iterable based on function result. 
     * Function result must implement Comparable
     * @param fun Function of one argument 
     * @param iter  
     * @return 
     */
    public static <T> T min(Object fun, Object iter) {
        return (T)min.invoke(iter);
    }   
    
    ////////////////////////////////////////////////////////////////////////////////
    // NOTEVERY
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn notEvery = new NotEvery();
    private static class NotEvery extends Fn {
        private NotEvery() {
            super(2,2);
        }
        @Override
        public Boolean doFunc(Object... args) {
            final Iterable<Object> coll = (Iterable<Object>)args[1];
            final IFn predicate = (IFn)args[0];
            for (Object o : coll) {
                if (isNil(predicate.invoke(o))) {
                    return true;
                }
            }
            return false;
        }
        
    }
    /**
     * Returns true if at least one object in iterable makes the predicate return a nil value
     * @param predicate 
     * @param iter  
     * @return true if predicate returns nil for any value
     */
    public static Boolean notEvery(Object predicate, Object iter) {
        return (Boolean)notEvery.invoke(predicate, iter);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // PARTITIONBY
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn partitionBy = new PartitionBy();
    private static class PartitionBy extends Fn {
        private PartitionBy() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            List<List<Object>> retVal = CollectionFactory.activeCollectionFactory.getListInstance();
            // this object will surely not be equal to anything else
            Object lastValue = new Object();
            IFn function = (IFn)args[0];
            for (Object elem : (Iterable<Object>)args[1]) {
                Object newValue = function.invoke(elem);
                // if IFn returned different value
                if (!lastValue.equals(newValue)) {
                    // remember new IFn value
                    lastValue = newValue;
                    // add a new sublist
                    List<Object> newSubList = CollectionFactory.activeCollectionFactory.getListInstance();
                    retVal.add(newSubList);
                    // add element into new sublist
                    newSubList.add(elem);
                } else {
                    // continuing same sublist
                    // add it to last sublist
                    retVal.get(retVal.size()-1).add(elem);
                }
            }
            return retVal;
        }
        
    }
    
    /**
     * Takes an iterable and returns a list of lists which contain the elements of the original iterable
     * divided into sublists. A new sublist is formed whenever function returns a different value for an iteration element
     * @param iterable
     * @param fun 
     * @return 
     */
    public static <T> List<List<T>> partitionBy(Object fun, Object iterable) {
        return (List<List<T>>)partitionBy.invoke(fun, iterable);
    }
    
    
    ////////////////////////////////////////////////////////////////////////////////
    // PICK
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn pick = new Pick();
    private static class Pick extends Fn {
        private Pick() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            final Iterable<Object> coll = (Iterable<Object>)args[1];
            final IFn filterFn = (IFn)args[0];
            for (Object o : coll) {
                if (!isNil(filterFn.invoke(o))) {
                    return o;
                }
            }
            return null;
        }
        
    };
    /**
     * Returns the first element from the iterable for which the predicate is non-nil (non-false and non-null).
     * Returns null is no such element exists. 
     * @param predicate Function of one element used to pick 
     * @param iter Iterable 
     * @return object
     */
    public static <T> T pick(Object predicate, Object iter) {
        return (T)pick.invoke(predicate, iter);
    }   
    
    /**
     * Returns the first non-nil (non-false and non-null) element from the varargs according to the predicate.
     * Returns null is no such element exists. 
     * @param predicate Function of one element used to pick 
     * @param elements objects to pick from going from first to last. 
     * @return object
     *
     */
    public static <T> T pickNonNull(Object predicate, Object... elements) {
        IFn fn = (IFn)predicate;
        for (Object o : elements) {
            if (!Fn.isNil(fn.invoke(o))) {
                return (T)o;
            }
        }
        return null;
    }
    ////////////////////////////////////////////////////////////////////////////////
    // RANGE
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn range = new Range();
    private static class Range extends Fn {
        private Range() {
            super(1,3);
        }
        @Override
        public Iterable<Long> doFunc(Object... args) {
            final long end;
            final long start;
            final long step;
            if (args.length == 1) {
                end = ((Number)args[0]).longValue();
                start = 0;
                step = 1;
            } else if (args.length == 2) {
                end = ((Number)args[1]).longValue();
                start = ((Number)args[0]).longValue();
                step = 1;
            } else if (args.length == 3){
                end = ((Number)args[1]).longValue();
                start = ((Number)args[0]).longValue();
                step = ((Number)args[2]).longValue();
            } else {
                end = 0;
                start = 0;
                step = 1;               
            }
            class ResIter extends SingleCheckIterator<Long> {
                long current = start - step;
                @Override
                protected boolean existsNext() {
                    if (step > 0) {
                        if (end - current > step) {
                            return true;
                        } else {
                            return false;
                        }
                    } else if (step < 0) {
                        if (end - current < step) {
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                        return true;
                    }
                }
                @Override
                protected Long generateNext() {
                    current += step;
                    return current;
                }
                @Override
                public SingleCheckIterator<Long> iterator() {
                    return new ResIter();
                }
            }
            return new ResIter();
        }
        
    }
    /**
     * Function returns a list of numbers from 0 (inclusive) to end (exclusive)
     * @param end - long - limit (exclusive) of the sequence of numbers
     * @return 
     */
    public static <T> Iterable<T> range(Object end) {
        return (Iterable<T>)range.invoke(end);
    }
    /**
     * Function returns a list of numbers from start (inclusive) to end (exclusive)
     * @param start - long - start of sequence of numbers (inclusive)
     * @param end - long - limit (exclusive) of the sequence of numbers
     * @return 
     */ 
    public static <T> Iterable<T> range(Object start, Object end) {
        return (Iterable<T>)range.invoke(start,end);
    }
    /**
     * Function returns a list of numbers from 0 (inclusive) to end (exclusive) incrementing by step
     * @param start - long - start of sequence of numbers (inclusive)
     * @param end - long - limit (exclusive) of the sequence of numbers
     * @param step - long - the amount of increment for each number in the sequence
     * @return 
     */ 
    public static <T> Iterable<T> range(Object start, Object end, Object step) {
        return (Iterable<T>)range.invoke(start, end, step);
    }

    ////////////////////////////////////////////////////////////////////////////////
    // REDUCE
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn reduce = new Reduce();
    private static class Reduce extends Fn {    
        private Reduce() {
            super(2,3);
        }
        @Override
        public Object doFunc(Object... args) {
            if (args.length == 2) {
                Object result = null;
                final Iterator<Object> coll = ((Iterable<Object>)args[1]).iterator();               
                final IFn reduceFn = (IFn)args[0];
                if (coll.hasNext()) {
                    result = coll.next();
                } else {
                    // no elements in the iter
                    return reduceFn.invoke();
                }
                if (!coll.hasNext()) {
                    // 1 element only in the iter
                    return result;
                }
                while (coll.hasNext()) {
                    result = reduceFn.invoke(result, coll.next());
                }
                return result;
            } else {
                Object result = args[1];
                final Iterator<Object> coll = ((Iterable<Object>)args[2]).iterator();               
                final IFn reduceFn = (IFn)args[0];
                if (!coll.hasNext()) {
                    return reduceFn.invoke(result);
                }
                while (coll.hasNext()) {
                    result = reduceFn.invoke(result, coll.next());
                }
                return result;  
            }
        }
        
    }
    /**
     * Reduces the iterable using operation i.e. it takes first two elements, applies the function
     * then it takes the result and applies function again on the result and the third element
     * then it repeats with fourth and so on. If iterable has 0 elements then function is
     * used with that many arguments. If iterable has 1 element only it is returned.
     * It returns the last result.
     * @param operation Function that can take 2 parameters 
     * @param iter Iterable 
     * @return object
     */
    
    public static <T> T reduce(Object operation, Object iter) {
        return (T)reduce.invoke(operation, iter);
    }   
    /**
     * Reduces the iterable using operation and starts with initValue. 
     * i.e. Function takes initValue and first element, applies the function
     * then it takes the result and applies function again on the result and the second element
     * then it repeats with third and so on. If iterable has 0 elements then function is
     * used on just initValue. It returns last result. 
     * @param operation Function that can take 2 parameters
     * @param initValue Initial value 
     * @param iter Iterable 
     * @return object
     */ 
    
    public static <T> T reduce(Object operation, Object initValue, Object iter) {
        return (T)reduce.invoke(operation, initValue, iter);
    }   

    ////////////////////////////////////////////////////////////////////////////////
    // REDUCTIONS
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn reductions = new Reductions();
    private static class Reductions extends Fn {    
        private Reductions() {
            super(2,3);
        }
        @Override
        public Iterable<? extends Object> doFunc(Object... args) {
            if (args.length == 2) {
                final Iterable<Object> coll = (Iterable<Object>)args[1];                
                final IFn reduceFn = (IFn)args[0];
                class ResIter extends SingleCheckIterator<Object> {
                    private Iterator<Object> iterator; 
                    private Object result = null;
                    boolean firstElement = true;
                    ResIter(Iterable<Object> itr) {
                        if (itr != null) {
                            iterator = itr.iterator();
                        } else {
                            iterator = null;
                        }
                    }
                    @Override
                    protected boolean existsNext() {
                        if (firstElement || iterator.hasNext()) {
                            return true;
                        } else {
                            result = null; // allow for temporary objects to be garbage collected
                            iterator = null;
                            return false;
                        }
                    }
                    @Override
                    protected Object generateNext() {
                        if (firstElement) {
                            firstElement = false;
                            if (iterator.hasNext()) {
                                result = iterator.next();
                                return result;
                            } else {
                                // no elements in the iter
                                return reduceFn.invoke();
                            }                                   
                        } else {
                            result = reduceFn.invoke(result, iterator.next());
                            return result;
                        }
                    }
                    @Override
                    public SingleCheckIterator<Object> iterator() {
                        return new ResIter(coll);
                    }
                    
                }
                return new ResIter(null);
            } else {
                final Object init = args[1];
                final Iterable<Object> coll = (Iterable<Object>)args[2];                
                final IFn reduceFn = (IFn)args[0];
                class ResIter extends SingleCheckIterator<Object> {
                    private Iterator<Object> iterator; 
                    private Object result = init;
                    private boolean firstElement = true;
                    ResIter(Iterable<Object> itr) {
                        if (itr != null) {
                            iterator = itr.iterator();
                        } else {
                            iterator = null;
                        }
                    }                   
                    @Override
                    protected boolean existsNext() {
                        if (firstElement || iterator.hasNext()) {
                            return true;
                        } else {
                            result = null; // allow for temporary objects to be garbage collected
                            iterator = null;
                            return false;
                        }
                    }
                    @Override
                    protected Object generateNext() {
                        if (firstElement) {
                            firstElement = false;
                        } else {
                            result = reduceFn.invoke(result, iterator.next());
                        }
                        return result;
                    }
                    @Override
                    public SingleCheckIterator<Object> iterator() {
                        return new ResIter(coll);
                    }
                    
                }
                return new ResIter(null);
            }
        }
        
    }
    /**
     * Reduces the iterable using operation i.e. it takes first two elements, applies the function
     * then it takes the result and applies function again on the result and the third element
     * then it repeats with fourth and so on. If iterable has only 1 or 0 elements then function is
     * used with that many arguments. It returns the last result.
     * @param operation Function that can take 2 parameters 
     * @param iter Iterable 
     * @return object
     */
    
    public static <T> Iterable<T> reductions(Object operation, Object iter) {
        return (Iterable<T>)reductions.invoke(operation, iter);
    }   
    /**
     * Reduces the iterable using operation and starts with initValue. 
     * i.e. Function takes initValue and first element, applies the function
     * then it takes the result and applies function again on the result and the second element
     * then it repeats with third and so on. If iterable has 0 elements then function is
     * used on just initValue. It returns last result. 
     * @param operation Function that can take 2 parameters
     * @param initValue Initial value 
     * @param iter Iterable 
     * @return object
     */ 
    
    public static <T> Iterable<T> reductions(Object operation, Object initValue, Object iter) {
        return (Iterable<T>)reductions.invoke(operation, initValue, iter);
    }   
    
    ////////////////////////////////////////////////////////////////////////////////
    // REMOVE
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn remove = new Remove();
    private static class Remove extends Fn {
        private Remove() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            final Iterator<Object> coll = ((Iterable<Object>)args[1]).iterator();
            final IFn filterFn = (IFn)args[0];
            Object nextRet;
            while(coll.hasNext()) {
                nextRet = coll.next();
                if (isNil((filterFn.invoke(nextRet)))) {
                    coll.remove();
                }
                
            }
            return args[1];
        }
    }
    /**
     * Returns new iterable without elements for which pred returns nil or false.
     * ALSO MODIFIES ORIGINATING ITERABLE. WILL FAIL IF REMOVE IS NOT IMPLEMENTED ON ITERATOR
     * This function is not lazy
     * (e.g. returned iterables of all these functions)
     * @param predicate Function used to filter 
     * @param iter Iterable 
     * @return Iterable
     */
    
    public static <T> Iterable<T> remove(Object predicate, Object iter) {
        return (Iterable<T>)remove.invoke(predicate, iter);
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    // REPEAT
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn repeat = new Repeat();
    private static class Repeat extends Fn {
        private Repeat() {
            super(1,2);
        }
        @Override
        public Iterable<? extends Object> doFunc(Object... args) {
            final Object obj;
            final Long num;
            if (args.length == 1) {
                obj = args[0];
                num = null;
            } else {
                obj = args[1];
                num = ((Number)args[0]).longValue();
            }

            class ResIter extends SingleCheckIterator<Object> {
                private Long count = 0L;
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter();
                }
                @Override
                protected boolean existsNext() {
                    return num == null || count < num;
                }
                @Override
                protected Object generateNext() {
                    if (num != null) {
                        count++;
                    }
                    return obj;
                }
            }
            return new ResIter();
        }
        
    }
    /**
     * Returns an endless iterable of object obj 
     * @param obj object to repeat
     * @return parameter obj
     */
    public static <T> Iterable<T> repeat(Object obj) {
        return (Iterable<T>)repeat.invoke(obj);
    }
    /**
     * Returns an iterable of object obj with num elements
     * @param num number of elements in iterable
     * @param obj object to repeat
     * @return parameter obj
     */
    public static <T> Iterable<T> repeat(Object num, Object obj) {
        return (Iterable<T>)repeat.invoke(num,obj);
    }       

    ////////////////////////////////////////////////////////////////////////////////
    // REPEATEDLY
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn repeatedly = new Repeatedly();
    private static class Repeatedly extends Fn {
        private Repeatedly() {
            super(1,2);
        }
        @Override
        public Iterable<? extends Object> doFunc(Object... args) {
            final IFn obj;
            final Long num;
            if (args.length == 1) {
                obj = (IFn)args[0];
                num = null;
            } else {
                obj = (IFn)args[1];
                num = ((Number)args[0]).longValue();
            }

            class ResIter extends SingleCheckIterator<Object> {
                private Long count = 0L;
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter();
                }
                @Override
                protected boolean existsNext() {
                    return num == null || count < num;
                }
                @Override
                protected Object generateNext() {
                    if (num != null) {
                        count++;
                    }
                    return obj.invoke();
                }
            }
            return new ResIter();
        }
        
    }
    /**
     * Returns an endless iterable of results of invoking the zero argument function 
     * @param obj function of zero arguments to repeatedly invoke
     * @return parameter obj
     */
    public static <T> Iterable<T> repeatedly(Object fn) {
        return (Iterable<T>)repeatedly.invoke(fn);
    }
    /**
     * Returns a num size iterable of results of invoking the zero argument function. 
     * @param num number of elements in iterable
     * @param obj function of zero arguments to repeatedly invoke
     * @return parameter obj
     */
    public static <T> Iterable<T> repeatedly(Object num, Object fn) {
        return (Iterable<T>)repeatedly.invoke(num,fn);
    }       

    ////////////////////////////////////////////////////////////////////////////////
    // SOME
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn some = new Some();
    private static class Some extends Fn { 
        private Some() {
            super(2,2);
        }
        @Override
        public Object doFunc(Object... args) {
            final Iterable<Object> coll = (Iterable<Object>)args[1];
            final IFn filterFn = (IFn)args[0];
            Object result;
            for (Object o : coll) {
                result = filterFn.invoke(o);
                if (!isNil(result)) {
                    return result;
                }
            }
            return null;
        }
        
    }
    /**
     * Returns the first predicate result on the iterable which is non-nil (non- false or null). 
     * @param predicate Function of one element - predicate 
     * @param iter Iterable 
     * @return First non-nil predicate result
     */
    

    public static <T> T some(Object predicate, Object iter) {
        return (T)some.invoke(predicate, iter);
    }   
    ////////////////////////////////////////////////////////////////////////////////
    // SUBSTRACT
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn substract = new Substract();
    private static class Substract extends Fn {
        private Substract() {
            super(3,4);
        }
        @Override
        protected Iterable<? extends Object> doFunc(Object... args) {
            final IFn fun = (IFn)args[0]; 
            final IFn otherFun;
            final Iterable<Object> substractee;
            final Iterable<Object> substractor;
            final boolean fnArityIsTwo = args.length == 3;
            if (fnArityIsTwo) {
                // in this mode we have 1 two argument fn
                // this fn is used as equality function to determine which
                // elements are equal and thus can be substracted
                // if function returns null or false for all substractor elements
                // then substractee element appears in returned iterable
                substractee = (Iterable<Object>)args[1];
                substractor = (Iterable<Object>)args[2];                
                class ResIter extends SingleCheckIterator<Object> {
                    // we take a new iterator
                    private Iterator<Object> substracteeIter;
                    private Object nextReturn;
                    ResIter(Iterable<Object> itr) {
                        if (itr != null) {
                            substracteeIter = itr.iterator();
                        } else {
                            substracteeIter = null;
                        }
                    }
                    @Override
                    protected boolean existsNext() {
                        while (substracteeIter.hasNext()) {
                            nextReturn = substracteeIter.next();
                            boolean allNil = true;
                            for (Object o : substractor) {
                                if (!isNil(fun.invoke(nextReturn, o))) {
                                    allNil = false;
                                    break;
                                }
                            }
                            if (allNil) {
                                return true;
                            }
                        }
                        substracteeIter = null; // allow for temporary objects to be garbage collected
                        nextReturn = null;
                        return false;
                    }
                    @Override
                    protected Object generateNext() {
                        return nextReturn;
                    }
                    @Override
                    public SingleCheckIterator<Object> iterator() {
                        return new ResIter(substractee);
                    }                   
                }
                return new ResIter(null);                               
            } else {
                // in this mode we have 2 single argument fns
                // first fn is used on first collection and second fn
                // on second collection
                // When result of fn invocations are equal the element of substractee
                // is not returned in resulting iterable
                // When second argument is null, we assume same function is used
                if (args[1] == null) {
                    otherFun = fun;
                } else {
                    otherFun = (IFn)args[1];
                }
                substractee = (Iterable<Object>)args[2];
                substractor = (Iterable<Object>)args[3];
                class ResIter extends SingleCheckIterator<Object> {

                    private Iterator<Object> substracteeIter;
                    private Set<Object> substractorValues;                        
                    ResIter(Iterable<Object> itr) {
                    
                        if (itr != null) {
                            substracteeIter = itr.iterator();
                            // load whole substractor and calculate their values
                            // put them into a set
                            substractorValues = new HashSet<Object>();
                            for (Object o : substractor) {
                                substractorValues.add(otherFun.invoke(o));
                            }
                        } else {
                            substracteeIter = null;
                            substractorValues = null;
                        }
                    }                   
                    // we take a new iterator  
                    private Object nextReturn;
                    @Override
                    protected boolean existsNext() {
                        while (substracteeIter.hasNext()) {
                            nextReturn = substracteeIter.next();
                            if (!substractorValues.contains(fun.invoke(nextReturn))) {
                                return true;
                            }
                        }
                        nextReturn = null;
                        substracteeIter = null;
                        substractorValues = null;
                        return false;
                    }
                    @Override
                    protected Object generateNext() {
                        return nextReturn;
                    }
                    @Override
                    public SingleCheckIterator<Object> iterator() {
                        return new ResIter(substractee);
                    }                   
                }
                return new ResIter(null);                               
            }
        }
    }
    /**
     * Substracts elements of substractor iterable from substractee iterable.
     * When the supplied function of 2 arguments returns non-nil value (not null or false)
     * two elements are deemed equal and thus the substractee one doesn't appear in the resulting
     * iterable.
     * 
     * @param <T>
     * @param fn Function of two arguments which returns non-nil value when two elements of
     * substractee and substractor are equal
     * @param substractee Iterable
     * @param substractor Iterable
     * @return iterable containing substractee elements not present in substractor according to function
     */
    public static <T> Iterable<T> substract(Object fn, Object substractee, Object substractor) {
        return (Iterable<T>)substract.invoke(fn, substractee, substractor);
    }
    /**
     * Substracts elements of substractor iterable from substractee iterable.
     * Supplied 2 functions of 1 argument are used on substractee and substractor elements
     * respectively. Elements of substractee whose function return equals any of the function
     * return or the substractor elements are not included in returned iterable.
     * If second function parameter is null then same function is used for both iterable parameters
     * 
     * @param <T>
     * @param fn1 Function of one argument 
     * @param fn2 Function of one argument, can be null 
     * @param substractee Iterable
     * @param substractor Iterable
     * @return iterable containing substractee elements not present in substractor
     */
    public static <T> Iterable<T> substract(Object fn1, Object fn2, Object substractee, Object substractor) {
        return (Iterable<T>)substract.invoke(fn1, fn2, substractee, substractor);
    }       
    ////////////////////////////////////////////////////////////////////////////////
    // TAKE
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn take = new Take();
    private static class Take extends Fn {  
        private Take() {
            super(2,2);
        }
        @Override
        public Iterable<? extends Object> doFunc(final Object... args) {
            final Long num = ((Number)args[0]).longValue();
            class ResIter extends SingleCheckIterator<Object> {
                private Iterator<Object> iter;
                private long idx = 0;
                ResIter(Iterable<Object> itr) {
                    if (itr != null) {
                        iter = itr.iterator();
                    } else {
                        iter = null;
                    }
                }
                @Override
                protected boolean existsNext() {
                    if (idx < num && iter.hasNext()) {
                        return true;
                    } else {
                        iter = null;
                        return false;
                    }
                
                }
                @Override
                protected Object generateNext() {
                    ++idx;
                    return iter.next();
                }
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter((Iterable<Object>)args[1]);
                }   
            }
            return new ResIter(null);
        }
        
    }
    /**
     * Returns an iterable which contains the first num elements of the supplied iterable. 
     * @param num Number of elements to take from the iterable 
     * @param iter  
     * @return 
     */
    
    public static <T> Iterable<T> take(Object num, Object iter) {
        return (Iterable<T>)take.invoke(num, iter);
    }   
    

    ////////////////////////////////////////////////////////////////////////////////
    // TAKEWHILE
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn takeWhile = new TakeWhile();
    private static class TakeWhile extends Fn {
        private TakeWhile() {
            super(2,2);
        }
        @Override
        public Iterable<? extends Object> doFunc(final Object... args) {
            final IFn pred = (IFn)args[0];
            class ResIter extends SingleCheckIterator<Object> {
                private Iterator<Object> iter;
                private boolean finished = false;
                private Object nextRet = null;
                ResIter(Iterable<Object> itr) {
                    if (itr != null) {
                        iter = itr.iterator();
                    } else {
                        iter = null;
                    }
                }               
                @Override
                protected boolean existsNext() {
                    if (!finished && iter.hasNext() && !isNil(pred.invoke((nextRet = iter.next())))) {
                        return true;
                    } else {
                        iter = null;  // allow for temporary objects to be garbage collected
                        nextRet = null;
                        return false;
                    }
                }
                @Override
                protected Object generateNext() {
                    return nextRet;
                }
                @Override
                public SingleCheckIterator<Object> iterator() {
                    return new ResIter((Iterable<Object>)args[1]);
                }   
            }
            return new ResIter(null);
        }
        
    }   
    
    /**
     * Returns an iterable which contains the first elements of the supplied iterable.
     * It keeps taking elements as long as predicate is true. If predicate becomes false, the returned
     * iterable ends (even though later elements might make it true again). 
     * @param predicate  
     * @param iter  
     * @return 
     */
    
    public static <T> Iterable<T> takeWhile(Object predicate, Object iter) {
        return (Iterable<T>)takeWhile.invoke(predicate, iter);
    }   
    
    ////////////////////////////////////////////////////////////////////////////////
    // VEC
    ////////////////////////////////////////////////////////////////////////////////
    public static final Fn vec = new Vec();
    private static class Vec extends Fn {
        @Override
        public Object doFunc(Object... args) {
            List<Object> retList = CollectionFactory.activeCollectionFactory.getListInstance(args.length);
            retList.addAll(Arrays.asList(args));
            return retList;
        }
        
    };
    /**
     * Returns new List containing the parameters
     * @param elements
     * @param  
     * @return 
     */
    
    public static <T> List<T> $V(Object... elements) {
        return (List<T>)vec.invoke(elements);
    }
    
}
